创建gtk.Window的截图
为了测试和记录,我想创建一个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
显然,我合并到基本示例中的截图示例使用的是gtk.gdk.Window,这个窗口是从gtk.gdk.Drawable继承而来的。
我有几个问题:
- 有没有其他方法可以让Pixbuf对象捕捉到这个窗口?
- 我能否将gtk.Window变成gtk.gdk.Window,或者在gtk.Window中找到gtk.gdk.Window的功能?
1 个回答
要理解的关键区别是,gtk.gdk.Window
不是大多数人想象中的“窗口”。它并不是一个图形界面元素,而只是屏幕上的一个区域,充当逻辑显示区,正如文档中所解释的。从这个角度来看,它更像是一个低级对象。
一个 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
,只要它的返回值等于 0
就可以。Python 默认情况下如果函数没有 return
语句,会返回 None
,所以默认情况下这个函数只会被调用一次。如果你希望这个函数在另一个时间间隔后再次被调用,可以返回 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()