Creating a screenshot of the gtk.Window file

For testing and documentation, I would like to take a screenshot of the gtk.Window object. At the moment I am following the basic pigt pattern. An example with my changes is as follows:

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 get an error when calling the get_from_drawable method.

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

Apparently the screenshot example that I combined into the base example uses gtk.gdk.Window, which inherits from gtk.gdk.Drawable.

My questions:

  • Is there any other way to get a Pixbuf object to capture a window?
  • Can I make gtk.Window gtk.gdk.Window or find the gtk.gdk.Window function in gtk.Window?
+6
source share
1 answer

The key difference is that gtk.gdk.Window not a "window" in the sense that most people think of. This is not a GUI element, it is just a section of the screen that acts as a logical display area, as described in the documentation . Thus, it is rather a low-level object.

An object

A gtk.Window (traditional "window") contains the window attribute, which is the gtk.gdk.Window object used by gtk.Window . It is created when the window is initialized (in this case, after win.show_all() ) is called).

Here is a revision of your code that saves the image correctly:

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

A timeout is necessary because, despite the fact that the gtk.gdk.Window object was created, the screen was still not drawn until gtk.main() working. If you read the pixel buffer immediately after win.show_all() , it will save the image, but the image will be the section of the screen that will occupy the window in the future. If you want to try this for yourself, replace the call with gobject.timeout_add simple call to drawWindow(win) .

Note that this method saves the image of the GTK window, but not the border of the window (therefore there is no title bar).

In addition, as a side point in defining a function called with gobject.timeout_add , you obviously do not need to use return False explicitly, it just needs to be evaluated to a value equivalent to 0 . Python returns None by default if there is no return in the function, so the function will only be called by default. If you want the function to be called again after another interval, return True .

Using signals

As noted in liberforce, instead of using a timeout, the best method would be to connect to the signals that GTK uses to transmit events (and state changes in general).

Everything that inherits from gobject.GObject (which all widgets do mostly) has a connect() function that is used to register a callback with a given signal. I tried several signals, and it seems that the one we want to use is the expose-event , which occurs at any time when the widget needs to be redrawn (including the first time it is drawn). Since we want the window to appear before our callback, we will use the connect_after() option.

Here is the code:

 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() 
+10
source

Source: https://habr.com/ru/post/897846/


All Articles