创建gtk.Window的屏幕截图

时间:2011-09-22 16:40:46

标签: 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()

调用get_from_drawable方法时,我最终会遇到错误。

TypeError: Gdk.Pixbuf.get_from_drawable() argument 1 must be gtk.gdk.Drawable, not gtk.Window

我合并到基本示例中的the screenshot example显然是使用了一个继承自gtk.gdk.Drawable的gtk.gdk.Window。

我的问题:

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

1 个答案:

答案 0 :(得分:11)

要理解的关键区别是gtk.gdk.Window不是大多数人想到的意义上的“窗口”。它不是GUI元素,它只是屏幕的一部分,充当逻辑显示区域,如explained in the documentation。通过这种方式,它更像是一个低级别的对象。

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()

超时是必要的,因为即使已经创建了gtk.gdk.Window对象,我仍然没有绘制屏幕,​​直到gtk.main()开始运行,我想。如果您在win.show_all()之后立即读取像素缓冲区,它将保存图像,但图像将是窗口稍后将占用的屏幕部分。如果您想亲自试用,只需拨打gobject.timeout_add,即可将来电替换为drawWindow(win)

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

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

使用信号

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

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

以下是修订后的代码:

connect_after()