For testing and documentation purposes I would like to create a screenshot of a gtk.Window object. I’m following a basic pygtk sample at the moment. The example with my modifications looks like the following:
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()
I’m ending up with an error when the get_from_drawable method is invoked.
TypeError: Gdk.Pixbuf.get_from_drawable() argument 1 must be gtk.gdk.Drawable, not gtk.Window
Apparently the screenshot example that I merged into the basic example is using a gtk.gdk.Window that inherits from gtk.gdk.Drawable.
My questions:
- Is there another way to make the Pixbuf object capture the window?
- Can I make a gtk.Window a gtk.gdk.Window or find the gtk.gdk.Window functionality within the gtk.Window?
The key difference to understand is that
gtk.gdk.Windowisn’t a “window” in the sense that most people think of. It’s not a GUI element, it’s just a section of the screen that acts as a logical display area, as explained in the documentation. In that way, it is more of a low-level object.A
gtk.Windowobject (the traditional “window”) contains awindowattribute which is thegtk.gdk.Windowobject that is used by thegtk.Window. It gets created when the window is initialized (in this case, after the call towin.show_all()).Here’s a revision of your code that saves the image correctly:
The timeout is necessary because even though the
gtk.gdk.Windowobject has been created, the screen still hasn’t been drawn, untilgtk.main()starts running, I think. If you read the pixel buffer right afterwin.show_all(), it will save an image, but the image will be the section of the screen that the window will later take up. If you want to try it for yourself, replace the call togobject.timeout_addwith a simple call todrawWindow(win).Note that this method saves an image of the GTK window, but not the border of the window (so, no title bar).
Also, as a side point, when defining a function that was called using
gobject.timeout_add, you don’t actually have to usereturn Falseexplicitly, it just has to evaluate to a value equivalent to0. Python returnNoneby default if there is noreturnstatement in the function, so the function will only be called once by default. If you want the function to be called again after another interval, returnTrue.Using signals
As noted by liberforce, instead of using a timeout, a better method would be to connect to the signals that are used by GTK to communicate events (and state changes in general).
Anything that inherits from
gobject.GObject(which all widgets do, basically) has aconnect()function which is used to register a callback with a given signal. I tried out a few signals, and it appears that the one we want to use isexpose-event, which occurs anytime a widget needs to be redrawn (including the first time it is drawn). Because we want the window to be drawn before our callback occurs, we’ll use theconnect_after()variant.Here’s the revised code: