Ключевым отличием для понимания является то, что gtk.gdk.Window
не является «окном» в том смысле, о котором думает большинство людей. Это не элемент графического интерфейса, это просто часть экрана, которая действует как логическая область отображения, как объяснено в документации 1003 *. Таким образом, это скорее низкоуровневый объект.
Объект 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 возвращает None
по умолчанию, если в функции нет оператора return
, поэтому функция будет вызываться только один раз по умолчанию. Если вы хотите, чтобы функция вызывалась снова через другой интервал, верните 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()