创建gtk.Window的屏幕截图

pum*_*azi 6 python gtk pygtk screenshot

出于测试和文档目的,我想创建一个gtk.Window对象的屏幕截图.我正在关注一个基本的pygtk样本.我修改的示例如下所示:

import gtk

def main():
    button = gtk.Button("Hello")
    scroll_win = gtk.ScrolledWindow()
    scroll_win.add(button)
    win = gtk.Window(gtk.WINDOW_TOPLEVEL)
    win.add(scroll_win)
    win.show_all()

    if win.is_drawable() is not True:
        raise RuntimeError("Not drawable?")

    width, height = win.get_size()
    pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
                            width, height)
    screenshot = pixbuf.get_from_drawable(win, win.get_colormap(),
                                          0, 0, 0, 0, width, height)
    screenshot.save('screenshot.png', 'png')

if __name__ == '__main__':
    main()
    gtk.main()
Run Code Online (Sandbox Code Playgroud)

调用get_from_drawable方法时,我最终会出错.

TypeError: Gdk.Pixbuf.get_from_drawable() argument 1 must be gtk.gdk.Drawable, not gtk.Window
Run Code Online (Sandbox Code Playgroud)

显然,我合并到基本示例中的屏幕截图示例是使用从gtk.gdk.Drawable继承的gtk.gdk.Window.

我的问题:

  • 还有另一种方法可以让Pixbuf对象捕获窗口吗?
  • 我可以创建gtk.Window gtk.gdk.Window或在gtk.Window中找到gtk.gdk.Window功能吗?

voi*_*hos 11

要理解的关键区别在于gtk.gdk.Window,大多数人都不会想到这个"窗口".它不是GUI元素,它只是屏幕的一部分,充当逻辑显示区域,如文档中所述.这样,它更像是一个低级别的对象.

gtk.Window对象(传统的"窗口")包含一个window属性,它是gtk.gdk.Window所使用的对象gtk.Window.它在窗口初始化时创建(在这种情况下,在调用之后win.show_all()).

以下是正确保存图像的代码修订版:

import gtk
import gobject

def main():
    button = gtk.Button("Hello")
    scroll_win = gtk.ScrolledWindow()
    scroll_win.add(button)
    win = gtk.Window(gtk.WINDOW_TOPLEVEL)
    win.add(scroll_win)
    win.show_all()

    # Set timeout to allow time for the screen to be drawn
    # before saving the window image
    gobject.timeout_add(1000, drawWindow, win)

def drawWindow(win):
    width, height = win.get_size()
    pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height)

    # Retrieve the pixel data from the gdk.window attribute (win.window)
    # of the gtk.window object
    screenshot = pixbuf.get_from_drawable(win.window, win.get_colormap(), 
                                          0, 0, 0, 0, width, height)

    screenshot.save('screenshot.png', 'png')

    # Return False to stop the repeating interval
    return False

if __name__ == '__main__':
    main()
    gtk.main()
Run Code Online (Sandbox Code Playgroud)

超时是必要的,因为即使gtk.gdk.Window创建了对象,屏幕仍然没有被绘制,直到gtk.main()开始运行,我想.如果您刚刚读取像素缓冲区win.show_all(),它将保存图像,但图像将是窗口稍后将占用的屏幕部分.如果您想亲自尝试,请将呼叫替换gobject.timeout_add为简单的呼叫drawWindow(win).

请注意,此方法保存GTK窗口的图像,但不保存窗口的边框(因此,没有标题栏).

另外,作为一个侧点,当定义一个被调用的函数时,gobject.timeout_add实际上你不必return False显式地使用它,它只需要计算一个等价的值0.None如果return函数中没有语句,Python 默认返回,因此该函数默认只调用一次.如果希望在另一个间隔后再次调用该函数,则返回True.

使用信号

正如liberforce所指出的,更好的方法是连接到GTK用来传递事件(以及一般状态变化)的信号,而不是使用超时.

任何继承的东西gobject.GObject(基本上所有小部件都有)具有一个connect()用于向给定信号注册回调的函数.我尝试了一些信号,看起来我们想要使用的是expose-event,任何时候需要重绘一个小部件(包括第一次绘制它)时都会发生这种信号.因为我们希望在回调发生之前绘制窗口,所以我们将使用connect_after()变体.

这是修改后的代码:

import gtk

# Don't use globals in a real application,
# better to encapsulate everything in a class
handlerId = 0

def main():
    button = gtk.Button("Hello")
    scroll_win = gtk.ScrolledWindow()
    scroll_win.add(button)
    win = gtk.Window(gtk.WINDOW_TOPLEVEL)
    win.add(scroll_win)

    # Connect to expose signal to allow time
    # for the window to be drawn
    global handlerId 
    handlerId = win.connect_after('expose-event', drawWindow)

    win.show_all()

def drawWindow(win, e):
    width, height = win.get_size()
    pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height)

    # Retrieve the pixel data from the gdk.window attribute (win.window)
    # of the gtk.window object
    screenshot = pixbuf.get_from_drawable(win.window, win.get_colormap(), 
                                          0, 0, 0, 0, width, height)
    screenshot.save('screenshot.png', 'png')

    # Disconnect this handler so that it isn't
    # repeated when the screen needs to be redrawn again
    global handlerId
    win.disconnect(handlerId)

if __name__ == '__main__':
    main()
    gtk.main()
Run Code Online (Sandbox Code Playgroud)