根窗口

PyCairo 教程的这个部分,我们将与根窗口打交道。根窗口就是桌面窗口,通常也是我们放置图标的地方。

控制根窗口是可能的。从程序员的角度来看,它仅仅是一种特殊的窗口。

透明窗口

我们的第一个例子将创建一个透明窗口。我们将看到窗口对象下面是什么东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This code example shows how to
create a transparent window.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import cairo
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.tran_setup()
self.init_ui()
def init_ui(self):
self.connect("draw", self.on_draw)
self.set_title("Transparent window")
self.resize(300, 250)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def tran_setup(self):
self.set_app_paintable(True)
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual != None and screen.is_composited():
self.set_visual(visual)
def on_draw(self, wid, cr):
cr.set_source_rgba(0.2, 0.2, 0.2, 0.4)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()

为了创建透明窗口,我们获取 screen 对象的 visual 值,并为我们的窗口设置它。在 on_draw() 方法中,我们在 screen 的 visual 对象上绘制。这创造了部分透明的错觉。

1
self.set_app_paintable(True)

我们必须将要绘制的应用设置为 on。

1
screen = self.get_screen()

get_screen() 方法返回 screen 对象。

1
visual = screen.get_rgba_visual()

从 screen 窗口,我们获取它的 visual。这个 visual 包含一些底层的显示信息。

1
2
if visual != None and screen.is_composited():
self.set_visual(visual)

不是所有的显示器都支持这种操作。然而,我们检查我们的屏幕是否支持组合,并且返回的 visual 不是 None。我们将屏幕的 visual 设置为我们的窗口的visual。

1
2
3
4
def on_draw(self, wid, cr):
cr.set_source_rgba(0.2, 0.2, 0.2, 0.4)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()

我们使用部分透明的 source 在屏幕窗口上绘制。cairo.OPERATOR_SOURCE 创建一个组合操作,我们以此用 source 来绘制,其中 source 为屏幕窗口。为了获取完全的透明,我们把 alpha 值设置为 0,或者使用 cairo.OPERATOR_CLEAR 操作符。

图:透明窗口

截屏

根窗口在截屏时也是必须的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This code example takes a screenshot.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
import gi
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk
import cairo
def main():
root_win = Gdk.get_default_root_window()
width = root_win.get_width()
height = root_win.get_height()
ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
pb = Gdk.pixbuf_get_from_window(root_win, 0, 0, width, height)
cr = cairo.Context(ims)
Gdk.cairo_set_source_pixbuf(cr, pb, 0, 0)
cr.paint()
ims.write_to_png("screenshot.png")
if __name__ == "__main__":
main()

这个例子截取整个屏幕的快照。

1
root_win = Gdk.get_default_root_window()

我们通过 Gdk.get_default_root_window() 方法调用获得根窗口。

1
2
width = root_win.get_width()
height = root_win.get_height()

我们确定根窗口的宽度和高度。

1
ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)

创建一个空的图像 surface。它的大小与根窗口一致。

1
pb = Gdk.pixbuf_get_from_window(root_win, 0, 0, width, height)

我们使用 Gdk.pixbuf_get_from_window() 方法调用由根窗口获取一个 pixbuf。pixbuf是在内存中描述图片的对象。它由 GTK 库使用。

1
2
3
cr = cairo.Context(ims)
Gdk.cairo_set_source_pixbuf(cr, pb, 0, 0)
cr.paint()

在上面的这几行代码中,我们基于前面创建的图片 surface 创建一个 Cairo 绘制上下文。我们把 pixbuf 放进绘制上下文,并且将它绘制到 surface 上。

1
ims.write_to_png("screenshot.png")

使 write_to_png() 方法将图片 surface 写入一个 PNG 图片文件。

展示消息

在第三个例子中,我们将在桌面窗口中显示一条信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This code example shows a message on the desktop
window.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, Pango
import cairo
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.setup()
self.init_ui()
def setup(self):
self.set_app_paintable(True)
self.set_type_hint(Gdk.WindowTypeHint.DOCK)
self.set_keep_below(True)
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual != None and screen.is_composited():
self.set_visual(visual)
def init_ui(self):
self.connect("draw", self.on_draw)
lbl = Gtk.Label()
text = "ZetCode, tutorials for programmers."
lbl.set_text(text)
fd = Pango.FontDescription("Serif 20")
lbl.modify_font(fd)
lbl.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("white"))
self.add(lbl)
self.resize(300, 250)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
cr.set_operator(cairo.OPERATOR_CLEAR)
cr.paint()
cr.set_operator(cairo.OPERATOR_OVER)
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
main()

这段代码在根窗口上显示一条消息标签。

1
self.set_app_paintable(True)

我们将管理应用窗口,所以使它为 paintable。

1
self.set_type_hint(Gdk.WindowTypeHint.DOCK)

实现这个窗口 hint 移除窗口边界和装饰。

1
self.set_keep_below(True)

我们使应用总是在底部,只在根窗口上方。

1
2
3
4
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual != None and screen.is_composited():
self.set_visual(visual)

我们将屏幕的 visual 设为我们的应用程序的 visual。

1
2
3
lbl = Gtk.Label()
text = "ZetCode, tutorials for programmers."
lbl.set_text(text)

我们在应用程序窗口上放一条消息标签。

1
2
3
fd = Pango.FontDescription("Serif 20")
lbl.modify_font(fd)
lbl.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("white"))

借助于 Pango 模块,我们改变文字的外观。

1
2
3
4
def on_draw(self, wid, cr):
cr.set_operator(cairo.OPERATOR_CLEAR)
cr.paint()
cr.set_operator(cairo.OPERATOR_OVER)

我们使用 cairo.OPERATOR_CLEAR 操作符清除窗口的背景。然后我们设置cairo.OPERATOR_CLEAR 来绘制标签 widget。

1
2
3
4
5
if __name__ == "__main__":
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
main()

有一个老 bug 使得我们无法通过 Ctrk+C 快捷键终止由终端启动的应用程序。添加这两行代码是对这种情况的一种workaround 做法。

图:根窗口上的消息

在这一章中,我们讨论了 PyCairo 中的桌面窗口。

原文

Done.

坚持原创技术分享,您的支持将鼓励我继续创作!