Python 创建gtk.窗口的屏幕截图
出于测试和文档的目的,我想创建一个gtk.Window对象的屏幕截图。我现在正在跟踪一个基本的pygtk样本。经过修改的示例如下所示:Python 创建gtk.窗口的屏幕截图,python,gtk,pygtk,screenshot,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
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.Drawable继承的gtk.gdk.Window
我的问题是:
- 是否有其他方法使Pixbuf对象捕获窗口
- 我可以将gtk.Window设置为gtk.gdk.Window还是在gtk.Window中找到gtk.gdk.Window功能
gtk.gdk.Window
不是大多数人想象中的“窗口”。它不是一个GUI元素,它只是屏幕的一部分,作为逻辑显示区域,如图所示。这样,它更像是一个低级对象
gtk.Window
对象(传统的“Window”)包含一个Window
属性,该属性是gtk.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\u add
的调用替换为对drawWindow(win)
的简单调用
请注意,此方法保存GTK窗口的图像,但不保存窗口的边框(因此,没有标题栏)
另外,作为补充,在定义使用gobject.timeout\u add
调用的函数时,实际上不必显式地使用return False
,只需将其求值为等于0
的值即可。如果函数中没有return
语句,则默认情况下Python returnNone
,因此默认情况下只调用一次函数。如果希望在另一个间隔后再次调用该函数,请返回True
使用信号
正如liberforce所指出的,与使用超时不同,更好的方法是连接到GTK用来传递事件(以及一般状态变化)的信号
任何继承自gobject.gobject
(所有小部件基本上都是这么做的)的东西都有一个connect()
函数,用于用给定信号注册回调。我尝试了几个信号,似乎我们想要使用的是expose event
,它在小部件需要重新绘制时(包括第一次绘制时)发生。因为我们希望在回调发生之前绘制窗口,所以我们将使用connect\u 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()
回答得很好。你解释的细节超出了我的要求;你还加了一些其他有用的东西。非常感谢。当我们确定gtk.gdk.Window已经创建时,超时应该由与信号的连接来代替。可能是
实现
或映射事件
,或类似的东西。@liberforce:是的,这是一个很好的观点。我更新了答案,加入了更多关于信号的信息。谢谢@voithos:我用了你的代码,但它不适用于alpha通道。请参阅我的问题:请注意,您不需要全局变量-只需使用lambda将handlerId
传递给回调即可获得后期绑定。例如:handlerId=win.connect\u在('expose-event',drawWindow,lambda:handlerId)
之后。drawWindow
然后获得一个额外的getId
参数,并以win.disconnect(getId())
结束。这避免了全局变量,而不需要整个专用类。