【问题标题】:Take a screenshot via a Python script on Linux在 Linux 上通过 Python 脚本截取屏幕截图
【发布时间】:2010-09-09 08:06:37
【问题描述】:

我想通过 python 脚本截取屏幕截图并不显眼地保存。

我只对 Linux 解决方案感兴趣,应该支持任何基于 X 的环境。

【问题讨论】:

  • 有什么理由不能使用scrot
  • 我很想检查以下建议方法的性能。
  • @Mark - :-/ 遗憾的是,Scrot 没有随 OS X 一起提供(我知道,这是一个 Linux 问题。通常适用于 Linux 的任何东西也可以逐字适用于 OS X。)
  • 嗯,没错,它是 OS X 上的屏幕截图。

标签: python linux screenshot


【解决方案1】:

这无需使用 scrot 或 ImageMagick 即可工作。

import gtk.gdk

w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
if (pb != None):
    pb.save("screenshot.png","png")
    print "Screenshot saved to screenshot.png."
else:
    print "Unable to get the screenshot."

借自http://ubuntuforums.org/showpost.php?p=2681009&postcount=5

【讨论】:

  • 这在使用 glade 的基于 GUI 的应用程序中不起作用,你能改进这段代码吗?
  • 当我执行此代码(在 virtualbox 中使用 linux mint 16)时,生成的图像完全是黑色的。你知道为什么吗?
  • 有时颜色的编码是关闭的。这很烦人。看看github.com/JDong820/neobot/blob/master/Linux/Robot/screen.py对你有没有帮助;注意对 get_rowstride 的调用。
【解决方案2】:

为了完整性: Xlib - 但捕获整个屏幕时有点慢:

from Xlib import display, X
import Image #PIL

W,H = 200,200
dsp = display.Display()
try:
    root = dsp.screen().root
    raw = root.get_image(0, 0, W,H, X.ZPixmap, 0xffffffff)
    image = Image.fromstring("RGB", (W, H), raw.data, "raw", "BGRX")
    image.show()
finally:
    dsp.close()

可以尝试在 PyXlib 的瓶颈文件中放入一些类型,然后使用 Cython 编译它。这样可以提高一点速度。


编辑: 我们可以用 C 编写函数的核心,然后从 ctypes 在 python 中使用它,这是我一起破解的:

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
//Compile hint: gcc -shared -O3 -lX11 -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c

void getScreen(const int, const int, const int, const int, unsigned char *);
void getScreen(const int xx,const int yy,const int W, const int H, /*out*/ unsigned char * data) 
{
   Display *display = XOpenDisplay(NULL);
   Window root = DefaultRootWindow(display);

   XImage *image = XGetImage(display,root, xx,yy, W,H, AllPlanes, ZPixmap);

   unsigned long red_mask   = image->red_mask;
   unsigned long green_mask = image->green_mask;
   unsigned long blue_mask  = image->blue_mask;
   int x, y;
   int ii = 0;
   for (y = 0; y < H; y++) {
       for (x = 0; x < W; x++) {
         unsigned long pixel = XGetPixel(image,x,y);
         unsigned char blue  = (pixel & blue_mask);
         unsigned char green = (pixel & green_mask) >> 8;
         unsigned char red   = (pixel & red_mask) >> 16;

         data[ii + 2] = blue;
         data[ii + 1] = green;
         data[ii + 0] = red;
         ii += 3;
      }
   }
   XDestroyImage(image);
   XDestroyWindow(display, root);
   XCloseDisplay(display);
}

然后是 python 文件:

import ctypes
import os
from PIL import Image

LibName = 'prtscn.so'
AbsLibPath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + LibName
grab = ctypes.CDLL(AbsLibPath)

def grab_screen(x1,y1,x2,y2):
    w, h = x2-x1, y2-y1
    size = w * h
    objlength = size * 3

    grab.getScreen.argtypes = []
    result = (ctypes.c_ubyte*objlength)()

    grab.getScreen(x1,y1, w, h, result)
    return Image.frombuffer('RGB', (w, h), result, 'raw', 'RGB', 0, 1)
    
if __name__ == '__main__':
  im = grab_screen(0,0,1440,900)
  im.show()

【讨论】:

  • 这很有价值,如果不是至少比其他答案更多的赞成票。扎实的工作和本地的!干杯!
  • 对于那些正在寻找快速方法的人:这种方法对于 1000 x 1000 大小的图片平均需要大约 25 毫秒。
  • @JHolta,你知道改变捕获图像质量的方法吗? (为了加快速度)
  • 这很好用,但我必须#include &lt;X11/Xutil.h&gt; 而不是#include &lt;X11/Xlib.h&gt;。同样为了编译,我不得不像这样将-lX11 移到最后:gcc -shared -O3 -Wall -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c -lX11
  • 经过进一步的压力测试,这在连续捕获许多帧时会以各种方式泄漏资源。您必须释放 X11 显示器,我相信默认情况下限制为 255 个客户端。即使您增加该限制,也不会释放图像/窗口,您最终也会耗尽内存。我在上面提交了解决这些问题的编辑。
【解决方案3】:

在一个班级中汇总所有答案。 输出 PIL 图像。

#!/usr/bin/env python
# encoding: utf-8
"""
screengrab.py

Created by Alex Snet on 2011-10-10.
Copyright (c) 2011 CodeTeam. All rights reserved.
"""

import sys
import os

import Image


class screengrab:
    def __init__(self):
        try:
            import gtk
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByGtk

        try:
            import PyQt4
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByQt

        try:
            import wx
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByWx

        try:
            import ImageGrab
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByPIL


    def getScreenByGtk(self):
        import gtk.gdk      
        w = gtk.gdk.get_default_root_window()
        sz = w.get_size()
        pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
        pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
        if pb is None:
            return False
        else:
            width,height = pb.get_width(),pb.get_height()
            return Image.fromstring("RGB",(width,height),pb.get_pixels() )

    def getScreenByQt(self):
        from PyQt4.QtGui import QPixmap, QApplication
        from PyQt4.Qt import QBuffer, QIODevice
        import StringIO
        app = QApplication(sys.argv)
        buffer = QBuffer()
        buffer.open(QIODevice.ReadWrite)
        QPixmap.grabWindow(QApplication.desktop().winId()).save(buffer, 'png')
        strio = StringIO.StringIO()
        strio.write(buffer.data())
        buffer.close()
        del app
        strio.seek(0)
        return Image.open(strio)

    def getScreenByPIL(self):
        import ImageGrab
        img = ImageGrab.grab()
        return img

    def getScreenByWx(self):
        import wx
        wx.App()  # Need to create an App instance before doing anything
        screen = wx.ScreenDC()
        size = screen.GetSize()
        bmp = wx.EmptyBitmap(size[0], size[1])
        mem = wx.MemoryDC(bmp)
        mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
        del mem  # Release bitmap
        #bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
        myWxImage = wx.ImageFromBitmap( myBitmap )
        PilImage = Image.new( 'RGB', (myWxImage.GetWidth(), myWxImage.GetHeight()) )
        PilImage.fromstring( myWxImage.GetData() )
        return PilImage

if __name__ == '__main__':
    s = screengrab()
    screen = s.screen()
    screen.show()

【讨论】:

  • 我不知道自这篇文章以来 wxWidgets 是否发生了变化,但 getScreenByWx 方法失败并出现 wx._core.PyNoAppError: The wx.App object must be created first!。有趣的是,如果您在 python shell 中逐行输入代码,但在脚本中它会失败,则代码可以正常工作。
  • 你应该测试你的代码!或者,不是您的,如果您要发布它...在getScreenByWx 中,您应该a)将myBitmap 替换为bmp 和b)将wx.App() 保存到变量中。在 getScreenByGtk 中,将 (pb != None) 替换为 pb is None。并且不要使用 Qt - 你不能创建两次 QApplication - 你的应用程序会在尝试第二次创建时崩溃。
【解决方案4】:

这个可以在 X11 上运行,也许也可以在 Windows 上运行(有人,请检查)。需要PyQt4

import sys
from PyQt4.QtGui import QPixmap, QApplication
app = QApplication(sys.argv)
QPixmap.grabWindow(QApplication.desktop().winId()).save('test.png', 'png')

【讨论】:

  • 请注意 PyQt 的许可,它比 Python 和 Qt 更严格。 riverbankcomputing.co.uk/software/pyqt/license
  • 这是在我的 Linux 安装“开箱即用”上运行的唯一解决方案。我不知道为什么,但我到处都有 PyQt4,而缺少 PyWX、PyGtk、ImageGrab。 - 谢谢 :)。
  • 代码刚刚运行(在 Windows 7 x64 - Python 2.7.5;Pythonxy 发行版上)。 Jpeg 也可用(例如 ... .save('d:/test.jpg', 'jpeg'))
【解决方案5】:

我有一个用于 scrot、imagemagick、pyqt、wx 和 pygtk 的包装器项目 (pyscreenshot)。 如果你有其中之一,你可以使用它。 所有解决方案都包含在此讨论中。

安装:

easy_install pyscreenshot

例子:

import pyscreenshot as ImageGrab

# fullscreen
im=ImageGrab.grab()
im.show()

# part of the screen
im=ImageGrab.grab(bbox=(10,10,500,500))
im.show()

# to file
ImageGrab.grab_to_file('im.png')

【讨论】:

  • ImportError: 无法导入名称 gtkpixbuf
  • 它给了我这个错误:pyscreenshot.tempexport.RunProgError: No protocol specified giblib error: Can't open X display. It *is* running, yeah?" timeout_happened=False&gt;
【解决方案6】:

使用wxPython的跨平台解决方案:

import wx
wx.App()  # Need to create an App instance before doing anything
screen = wx.ScreenDC()
size = screen.GetSize()
bmp = wx.EmptyBitmap(size[0], size[1])
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
del mem  # Release bitmap
bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)

【讨论】:

【解决方案7】:
import ImageGrab
img = ImageGrab.grab()
img.save('test.jpg','JPEG')

这需要 Python 图像库

【讨论】:

【解决方案8】:

你可以用这个

import os
os.system("import -window root screen_shot.png")

【讨论】:

  • 一旦你可以从后台程序中获取图像,这是一个很好的方法,但很高兴知道如果程序聚焦它会返回异常。
【解决方案9】:

我无法在 Linux 中使用 pyscreenshot 或 scrot 截屏,因为 pyscreenshot 的输出只是一个黑屏 png 图像文件。

但是感谢上帝,还有另一种非常简单的方法可以在 Linux 中截取屏幕截图,而无需安装任何东西。只需将以下代码放在您的目录中并使用python demo.py运行即可

import os
os.system("gnome-screenshot --file=this_directory.png")

gnome-screenshot --help 也有许多可用选项

Application Options:
  -c, --clipboard                Send the grab directly to the clipboard
  -w, --window                   Grab a window instead of the entire screen
  -a, --area                     Grab an area of the screen instead of the entire screen
  -b, --include-border           Include the window border with the screenshot
  -B, --remove-border            Remove the window border from the screenshot
  -p, --include-pointer          Include the pointer with the screenshot
  -d, --delay=seconds            Take screenshot after specified delay [in seconds]
  -e, --border-effect=effect     Effect to add to the border (shadow, border, vintage or none)
  -i, --interactive              Interactively set options
  -f, --file=filename            Save screenshot directly to this file
  --version                      Print version information and exit
  --display=DISPLAY              X display to use

【讨论】:

    【解决方案10】:

    有点晚了,不过没关系

    import autopy
    import time
    time.sleep(2)
    b = autopy.bitmap.capture_screen()
    b.save("C:/Users/mak/Desktop/m.png")
    

    【讨论】:

      【解决方案11】:

      这个Autopy有一个python包

      位图模块可以抓屏(bitmap.capture_screen) 它是多平台的(Windows、Linux、Osx)。

      【讨论】:

        【解决方案12】:

        对于 ubuntu 这个工作对我来说,你可以用这个截取选择窗口的截图:

        import gi
        gi.require_version('Gtk', '3.0')
        gi.require_version('Gdk', '3.0')
        
        from gi.repository import Gdk
        from gi.repository import GdkPixbuf
        import numpy as np
        from Xlib.display import Display
        
        #define the window name
        window_name = 'Spotify'
        
        #define xid of your select 'window'
        def locate_window(stack,window):
            disp = Display()
            NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
            WM_NAME = disp.intern_atom('WM_NAME') 
            name= []
            for i, w in enumerate(stack):
                win_id =w.get_xid()
                window_obj = disp.create_resource_object('window', win_id)
                for atom in (NET_WM_NAME, WM_NAME):
                    window_name=window_obj.get_full_property(atom, 0)
                    name.append(window_name.value)
            for l in range(len(stack)):
                if(name[2*l]==window):
                    return stack[l]
        
        window = Gdk.get_default_root_window()
        screen = window.get_screen()
        stack = screen.get_window_stack()
        myselectwindow = locate_window(stack,window_name)
        img_pixbuf = Gdk.pixbuf_get_from_window(myselectwindow,*myselectwindow.get_geometry()) 
        

        将pixbuf转换成数组

        def pixbuf_to_array(p):
            w,h,c,r=(p.get_width(), p.get_height(), p.get_n_channels(), p.get_rowstride())
            assert p.get_colorspace() == GdkPixbuf.Colorspace.RGB
            assert p.get_bits_per_sample() == 8
            if  p.get_has_alpha():
                assert c == 4
            else:
                assert c == 3
            assert r >= w * c
            a=np.frombuffer(p.get_pixels(),dtype=np.uint8)
            if a.shape[0] == w*c*h:
                return a.reshape( (h, w, c) )
            else:
                b=np.zeros((h,w*c),'uint8')
                for j in range(h):
                    b[j,:]=a[r*j:r*j+w*c]
                return b.reshape( (h, w, c) )
        
        beauty_print = pixbuf_to_array(img_pixbuf)
        

        【讨论】:

          【解决方案13】:

          来自this thread

           import os
           os.system("import -window root temp.png")
          

          【讨论】:

            【解决方案14】:

            这是一个老问题。我想用新工具来回答。

            适用于 python 3(应该适用于 python 2,但我尚未测试)和 PyQt5。

            最小的工作示例。将其复制到python shell并得到结果。

            from PyQt5.QtWidgets import QApplication
            app = QApplication([])
            screen = app.primaryScreen()
            screenshot = screen.grabWindow(QApplication.desktop().winId())
            screenshot.save('/tmp/screenshot.png')
            

            【讨论】:

            • 你有完成这个功能的平均时间吗?如果值得的话只是兴趣
            • @Mrlenny 300 ms(完整代码),165 ms(最后三行代码)。
            【解决方案15】:

            试试看:

            #!/usr/bin/python
            
            import gtk.gdk
            import time
            import random
            import socket
            import fcntl
            import struct
            import getpass
            import os
            import paramiko     
            
            while 1:
                # generate a random time between 120 and 300 sec
                random_time = random.randrange(20,25)
                # wait between 120 and 300 seconds (or between 2 and 5 minutes) 
            
                print "Next picture in: %.2f minutes" % (float(random_time) / 60)
            
                time.sleep(random_time)
                w = gtk.gdk.get_default_root_window()   
                sz = w.get_size()
                print "The size of the window is %d x %d" % sz
                pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
                pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
                ts = time.asctime( time.localtime(time.time()) )
                date = time.strftime("%d-%m-%Y")
                timer = time.strftime("%I:%M:%S%p")
                filename = timer
                filename += ".png"
            
                if (pb != None):
                    username = getpass.getuser() #Get username
                    newpath = r'screenshots/'+username+'/'+date #screenshot save path
                    if not os.path.exists(newpath): os.makedirs(newpath)
                    saveas = os.path.join(newpath,filename)
                    print saveas
                    pb.save(saveas,"png")
                else:
                    print "Unable to get the screenshot."
            

            【讨论】:

            • 这是什么垃圾?一半的导入是无用的,有一个永远不会退出的while 循环(并使用1 而不是True),有if (pb != None): 而不仅仅是if pb:,有一些毫无意义的原始字符串。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-12-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-03-18
            • 1970-01-01
            相关资源
            最近更新 更多