Python GTK + 3教程

发布版本:3.4
日期:2018年5月30日
版权:GNU自由文档许可证1.3,没有不变部分,没有封面文本,也没有封底文本

本教程介绍了如何使用Python编写GTK + 3应用程序。

在完成本教程之前,建议您合理掌握Python编程语言。 GUI编程引入了与标准输出(控制台/终端)交互相比的新问题。 您需要知道如何创建和运行Python文件,了解基本的解释器错误,以及使用字符串,整数,浮点数和布尔值。 对于本教程中更高级的小部件,需要有关于列表和元组的丰富知识。

尽管本教程描述了GTK + 3中最重要的类和方法,但它不应作为API参考。 请参阅GTK + 3参考手册了解API的详细描述。 还有一个可用的特定于Python的参考

内容:

安装¶ T0>

在我们开始实际编码之前的第一步包括设置PyGObject及其依赖关系。 PyGObject是一个Python模块,它使开发人员能够在Python中访问基于GObject的库,例如GTK +。 它完全支持GTK +版本3或更高版本。 如果您想在应用程序中使用GTK + 2,请改为使用PyGTK

依赖¶ T0>

  • GTK + 3
  • Python 2(2.6或更高版本)或Python 3(3.1或更高版本)
  • GObject的内省

预先构建的软件包

几乎所有主要的Linux发行版都打包了最近版本的PyGObject及其依赖项。 因此,如果您使用Linux,您可能会从您的发行版的官方存储库安装软件包开始。

从源安装

从源代码安装PyGObject最简单的方法是使用JHBuild 它旨在轻松构建源代码包并发现需要构建的依赖关系以及以何种顺序。 要设置JHBuild,请按照JHBuild手册

成功安装JHBuild后,从[1]下载最新的配置。 将后缀.modules的文件复制到JHBuild的模块目录,并将sample-tarball.jhbuildrc文件复制到〜/ .jhbuildrc

如果您之前没有做过,请运行以下命令验证您的编译环境是否正确安装:

$ jhbuild sanitycheck

它将打印您的系统上当前缺少但需要构建的任何应用程序和库。 您应该使用发行版的软件包存储库安装这些软件。 GNOME wiki上维护了不同发行版的软件包名称列表。 再次运行上面的命令以确保所需的工具存在。

执行以下命令将构建PyGObject及其所有依赖项:

$ jhbuild build pygobject

最后,您可能还想从源代码安装GTK +:

$ jhbuild build gtk+-3

要使用JHBuild使用的相同环境启动shell,请运行:

$ jhbuild shell
[1]https://download.gnome.org/teams/releng/

入门

简单示例

要开始我们的教程,我们可以创建最简单的程序。 该程序将创建一个空的200 x 200像素窗口。

_images/simple_example.png
1
2
3
4
5
6
7
8
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

win = Gtk.Window()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

现在我们将解释这个例子的每一行。

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

一开始,我们必须导入Gtk模块才能访问GTK +的类和函数。 由于用户的系统可以同时安装多个版本的GTK +,我们希望确保当我们导入Gtk时,它引用GTK + 3而不是任何其他版本的库,这就是语句的用途。 gi.require_version('Gtk', '3.0')

下一行创建一个空的窗口。

win = Gtk.Window()

随后连接到窗口的删除事件,以确保如果我们单击x关闭窗口,应用程序将终止。

win.connect("destroy", Gtk.main_quit)

在下一步我们显示窗口。

win.show_all()

最后,我们启动当窗口关闭时退出的GTK +处理循环(请参见第5行)。

Gtk.main()

要运行该程序,请打开一个终端,切换到该文件的目录,然后输入:

python simple_example.py

扩展示例

对于一些更有用的东西,这里是经典的“Hello World”程序的PyGObject版本。

_images/extended_example.png
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class MyWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Hello World")

        self.button = Gtk.Button(label="Click Here")
        self.button.connect("clicked", self.on_button_clicked)
        self.add(self.button)

    def on_button_clicked(self, widget):
        print("Hello World")

win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

这个例子与我们子类Gtk.Window定义我们自己的MyWindow类的简单例子不同。

class MyWindow(Gtk.Window):

在类的构造函数中,我们必须调用超类的构造函数。 另外,我们告诉它将属性title的值设置为Hello World

        Gtk.Window.__init__(self, title="Hello World")

接下来的三行用于创建按钮小部件,连接到点击的信号并将其作为子项添加到顶级窗口。

        self.button = Gtk.Button(label="Click Here")
        self.button.connect("clicked", self.on_button_clicked)
        self.add(self.button)

因此,如果你点击按钮,方法on_button_clicked()将被调用。

    def on_button_clicked(self, widget):
        print("Hello World")

类之外的最后一个块与上面的简单示例非常相似,但不是创建通用Gtk.Window类的实例,而是创建MyWindow

基本¶ T0>

本节将介绍GTK +的一些最重要的方面。

主循环和信号

像大多数GUI工具包一样,GTK +使用事件驱动的编程模型。 当用户什么都不做时,GTK +会在主循环中等待输入。 如果用户执行某些操作 - 比如说,单击鼠标 - 则主循环“唤醒”并将事件传递给GTK +。

当小部件收到事件时,他们会经常发出一个或多个信号。 信号通过调用您连接到信号的函数来唤醒您的程序:“起来干活!” 这些功能通常称为回调 当您调用回调函数时,通常会采取一些操作 - 例如,当单击“打开”按钮时,可能会显示文件选择器对话框。 回调完成后,GTK +将返回主循环并等待更多用户输入。

一个通用的例子是:

handler_id = widget.connect("event", callback, data)

首先,widget是我们之前创建的小部件的一个实例。 接下来,我们感兴趣的事件。 每个小部件都有其特定的事件可能发生。 例如,如果你有一个按钮,你通常要连接到“clicked”事件。 这意味着当点击按钮时,信号被发出。 第三,callback参数是回调函数的名称。 它包含在发出指定类型的信号时运行的代码。 最后,data参数包含信号发出时应传递的任何数据。 但是,这个参数是完全可选的,如果不需要,可以省略。

该函数返回一个标识此特定信号回调对的数字。 需要从信号断开连接,以便在未来或当前正在连接的信号发射期间不会调用回叫功能。

widget.disconnect(handler_id)

如果由于某种原因(例如,处理程序是使用Gtk.Builder.connect_signals()安装的)丢失了“handler_id”,则仍然可以使用函数disconnect_by_func()

widget.disconnect_by_func(callback)

应用程序应连接到顶级窗口的“销毁”信号。 它在对象被销毁时发出,因此当用户请求顶层窗口关闭时,此信号的默认处理程序会销毁窗口,但不会终止应用程序。 将顶级窗口的“销毁”信号连接到函数Gtk.main_quit()将导致期望的行为。

window.connect("destroy", Gtk.main_quit)

调用Gtk.main_quit()会使Gtk.main()中的主循环返回。

属性¶ T0>

属性描述小部件的配置和状态。 至于信号,每个小部件都有自己的一组特性。 例如,按钮具有属性“标签”,其中包含按钮内部的标签小部件的文本。 在创建小部件实例时,您可以将任意数量的属性的名称和值指定为关键字参数。 要使用文本“Hello World”和25度角创建与右侧对齐的标签,请使用:

label = Gtk.Label(label="Hello World", angle=25, halign=Gtk.Align.END)

相当于

label = Gtk.Label()
label.set_label("Hello World")
label.set_angle(25)
label.set_halign(Gtk.Align.END)

您可以通过“props”属性(如widget.props.prop_name = )来获取和设置gobject属性,而不是使用getter和setter。 T3> T0>。 这相当于更详细的widget.get_property("prop-name")widget.set_property(“prop-name”, value) T4> T2>。

要查看GTK正在运行的版本中的某个窗口小部件的可用属性,您可以“dir”它的“props”属性:

widget = Gtk.Box()
print(dir(widget.props))

这将在控制台中打印Gtk.Box所具有的属性列表。

如何处理字符串

本节介绍如何在Python 2.x,Python 3.x和GTK +中表示字符串,并讨论使用字符串时出现的常见错误。

定义¶ T0>

概念上,字符串是诸如'A','B','C'或'É'的字符列表。 Characters are abstract representations and their meaning depends on the language and context they are used in. Unicode标准描述了如何用代码点表示字符。 例如,上面的字符分别用代码点U + 0041,U + 0042,U + 0043和U + 00C9表示。 基本上,代码点是从0到0x10FFFF范围内的数字。

如前所述,作为代码点列表的字符串表示是抽象的。 为了将这个抽象表示转换为一个字节序列,Unicode字符串必须被编码 最简单的编码形式是ASCII,其执行方式如下:

  1. 如果代码点<128,则每个字节与代码点的值相同。
  2. 如果代码点为128或更大,则Unicode字符串不能用此编码表示。 (在这种情况下,Python引发了一个UnicodeEncodeError异常。)

虽然ASCII编码很容易应用,但它只能编码128个不同的字符,这是不够的。 解决这个问题的最常用的编码之一是UTF-8(它可以处理任何Unicode代码点)。 UTF代表“Unicode转换格式”,“8”表示编码中使用8位数字。

Python 2

Python 2.x的Unicode支持

Python 2带有两种不同的可用于表示字符串的对象,strunicode 后者的实例用于表示Unicode字符串,而str类型的实例是字节表示(编码字符串)。 在这种情况下,Python将Unicode字符串表示为16位或32位整数,具体取决于Python解释器的编译方式。 Unicode字符串可以通过unicode.encode()转换为8位字符串:

>>> unicode_string = u"Fu\u00dfb\u00e4lle"
>>> print unicode_string
Fußbälle
>>> type(unicode_string)
<type 'unicode'>
>>> unicode_string.encode("utf-8")
'Fu\xc3\x9fb\xc3\xa4lle'

Python的8位字符串有一个使用给定编码解释字符串的str.decode()方法:

>>> utf8_string = unicode_string.encode("utf-8")
>>> type(utf8_string)
<type 'str'>
>>> u2 = utf8_string.decode("utf-8")
>>> unicode_string == u2
True

不幸的是,如果8位字符串恰好只包含7位(ASCII)字节,但Python 2.x允许您混合unicodestr,但会得到UnicodeDecodeError如果它包含非ASCII值:

>>> utf8_string = " sind rund"
>>> unicode_string + utf8_string
u'Fu\xdfb\xe4lle sind rund'
>>> utf8_string = " k\xc3\xb6nnten rund sein"
>>> print utf8_string
 könnten rund sein
>>> unicode_string + utf8_string
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2:
ordinal not in range(128)

GTK +中的Unicode

GTK +为所有文本使用UTF-8编码的字符串。 这意味着如果您调用一个返回字符串的方法,您将始终获得str类型的实例。 这同样适用于期望一个或多个字符串作为参数的方法,它们必须是UTF-8编码的。 但是,为了方便PyGObject会自动将任何unicode实例转换为str,如果提供为参数:

>>> from gi.repository import Gtk
>>> label = Gtk.Label()
>>> unicode_string = u"Fu\u00dfb\u00e4lle"
>>> label.set_text(unicode_string)
>>> txt = label.get_text()
>>> type(txt), txt
(<type 'str'>, 'Fu\xc3\x9fb\xc3\xa4lle')
>>> txt == unicode_string
__main__:1: UnicodeWarning: Unicode equal comparison failed to convert
both arguments to Unicode - interpreting them as being unequal
False

注意最后的警告。 Although we called Gtk.Label.set_text() with a unicode instance as argument, Gtk.Label.get_text() will always return a str instance. 相应地,txtunicode_string不等于

如果您想使用gettext将程序国际化,这一点尤其重要。 您必须确保gettext将为所有语言返回UTF-8编码的8位字符串。 通常,建议不要在GTK +应用程序中使用unicode对象,并且只使用UTF-8编码的str对象,因为GTK +没有完全与unicode兼容。 否则,每次调用GTK +方法时,都必须将返回值解码为Unicode字符串:

>>> txt = label.get_text().decode("utf-8")
>>> txt == unicode_string
True

Python 3

Python 3.x的Unicode支持

从Python 3.0开始,所有字符串都以Unicode存储在str类型的实例中。 Encoded strings on the other hand are represented as binary data in the form of instances of the bytes type. 概念上,str指的是文字,而bytes指的是数据 使用str.encode()str转到bytesbytes.decode() bytesstr

另外,不再可能将Unicode字符串与编码字符串混合,因为它会导致TypeError

>>> text = "Fu\u00dfb\u00e4lle"
>>> data = b" sind rund"
>>> text + data
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'bytes' object to str implicitly
>>> text + data.decode("utf-8")
'Fußbälle sind rund'
>>> text.encode("utf-8") + data
b'Fu\xc3\x9fb\xc3\xa4lle sind rund'

GTK +中的Unicode

因此,如果您将一个字符串传递给一个方法或者一个方法返回一个字符串,则PyGObject将自动编码/解码为UTF-8 /从UTF-8自动编码/解码,因此事情更清晰并与Python 3.x保持一致。 字符串或文本将始终仅表示为str的实例:

>>> from gi.repository import Gtk
>>> label = Gtk.Label()
>>> text = "Fu\u00dfb\u00e4lle"
>>> label.set_text(text)
>>> txt = label.get_text()
>>> type(txt), txt
(<class 'str'>, 'Fußbälle')
>>> txt == text
True

参考¶ T0>

Python 3.0中的新功能描述了清晰区分文本和数据的新概念。

Unicode HOWTO讨论了Python 2.x对Unicode的支持,并解释了人们在尝试使用Unicode时经常遇到的各种问题。

针对Python 3.x的Unicode HOWTO讨论了Python 3.x中的Unicode支持。

UTF-8编码表和Unicode字符包含一列Unicode代码点及其各自的UTF-8编码。

布局容器

虽然许多GUI工具包要求您使用绝对定位精确地将窗口小部件放置在窗口中,但GTK +使用不同的方法。 而不是在窗口中指定每个小部件的位置和大小,您可以按行,列和/或表格排列小部件。 您的窗口大小可以根据它包含的小部件的大小自动确定。 而窗口小部件的大小反过来又取决于它们包含的文本数量,或者您指定的最小和最大尺寸,以及/或者您如何请求可用空间​​应该在多组窗口小部件之间共享。 您可以通过为每个小部件指定填充距离和居中值来完善布局。 当用户操作窗口时,GTK +会使用所有这些信息来合理调整和重新定位所有内容。

GTK +使用容器分层安排小部件。 它们对最终用户是不可见的,并且被插入到窗口中,或者被放置在彼此之中以布置组件。 有两种类型的容器:一种是单后代容器,是Gtk.Bin的子类;另一种是多后代容器,是Gtk.Container的子类。 最常用的是垂直或水平方块(Gtk.Box)和网格(Gtk.Grid)。

¶ T0>

盒子是我们可以包装我们的小部件的隐形容器。 当将小部件放置到水平框中时,根据Gtk.Box.pack_start()Gtk.Box.pack_end() 在垂直框中,小部件从上到下打包,反之亦然。 您可以在其他框内或旁边使用任意组合框来创建所需的效果。

实施例¶ T0>

我们来看看带有两个按钮的扩展示例的稍微修改后的版本。

_images/layout_box_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class MyWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Hello World")

        self.box = Gtk.Box(spacing=6)
        self.add(self.box)

        self.button1 = Gtk.Button(label="Hello")
        self.button1.connect("clicked", self.on_button1_clicked)
        self.box.pack_start(self.button1, True, True, 0)

        self.button2 = Gtk.Button(label="Goodbye")
        self.button2.connect("clicked", self.on_button2_clicked)
        self.box.pack_start(self.button2, True, True, 0)

    def on_button1_clicked(self, widget):
        print("Hello")

    def on_button2_clicked(self, widget):
        print("Goodbye")

win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

首先,我们创建一个水平方向的盒子容器,其中6个像素放置在孩子之间。 此框成为顶层窗口的子项。

        self.box = Gtk.Box(spacing=6)
        self.add(self.box)

随后,我们将两个不同的按钮添加到盒子容器中。

        self.button1 = Gtk.Button(label="Hello")
        self.button1.connect("clicked", self.on_button1_clicked)
        self.box.pack_start(self.button1, True, True, 0)

        self.button2 = Gtk.Button(label="Goodbye")
        self.button2.connect("clicked", self.on_button2_clicked)
        self.box.pack_start(self.button2, True, True, 0)

Gtk.Box.pack_start()小部件从左到右定位时,Gtk.Box.pack_end()将它们从右到左放置。

¶ T0>

Gtk.Grid是一个容器,它将子窗口小部件排列成行和列,但不需要在构造函数中指定尺寸。 使用Gtk.Grid.attach()添加孩子。 它们可以跨越多行或多列。 Gtk.Grid.attach()方法有五个参数:

  1. child参数是要添加的Gtk.Widget
  2. left是将child的左侧附加到的列号。
  3. top指示将child的顶端附加到的行号。
  4. widthheight指示child将要跨过的列数,以及child将分别跨越。

也可以使用Gtk.Grid.attach_next_to()在现有孩子旁边添加一个孩子,该孩子还需要五个参数:

  1. child是要添加的Gtk.Widget,如上所述。
  2. siblingself(a Gtk.Grid实例)或None的现有子件。 The child widget will be placed next to sibling, or if sibling is None, at the beginning or end of the grid.
  3. side是一个Gtk.PositionType,指示child紧邻的sibling的一侧。
  4. widthheight分别表示child小部件将跨越的列和行数。

最后,通过使用Gtk.Grid.add(),可以像Gtk.Box一样使用Gtk.Grid,在由“orientation”属性确定的方向上彼此交互(默认为Gtk.Orientation.HORIZONTAL)。

实施例¶ T0>

_images/layout_grid_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class GridWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Grid Example")

        grid = Gtk.Grid()
        self.add(grid)

        button1 = Gtk.Button(label="Button 1")
        button2 = Gtk.Button(label="Button 2")
        button3 = Gtk.Button(label="Button 3")
        button4 = Gtk.Button(label="Button 4")
        button5 = Gtk.Button(label="Button 5")
        button6 = Gtk.Button(label="Button 6")

        grid.add(button1)
        grid.attach(button2, 1, 0, 2, 1)
        grid.attach_next_to(button3, button1, Gtk.PositionType.BOTTOM, 1, 2)
        grid.attach_next_to(button4, button3, Gtk.PositionType.RIGHT, 2, 1)
        grid.attach(button5, 1, 2, 1, 1)
        grid.attach_next_to(button6, button5, Gtk.PositionType.RIGHT, 1, 1)

win = GridWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

列表框¶ T0>

Gtk.ListBox是一个包含Gtk.ListBoxRow子元素的垂直容器。 这些行可以动态排序和过滤,并且可以根据行内容动态添加标题。 它还允许像典型列表一样的键盘和鼠标导航和选择。

使用Gtk.ListBox通常是Gtk.TreeView的替代方法,尤其是当列表内容的布局比Gtk.CellRenderer

Although a Gtk.ListBox must have only Gtk.ListBoxRow children, you can add any kind of widget to it via Gtk.Container.add() and a Gtk.ListBoxRow widget will automatically be inserted between the list and the widget.

实施例¶ T0>

_images/listbox_example.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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class ListBoxRowWithData(Gtk.ListBoxRow):
    def __init__(self, data):
        super(Gtk.ListBoxRow, self).__init__()
        self.data = data
        self.add(Gtk.Label(data))

class ListBoxWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="ListBox Demo")
        self.set_border_width(10)

        box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(box_outer)

        listbox = Gtk.ListBox()
        listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        box_outer.pack_start(listbox, True, True, 0)

        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        hbox.pack_start(vbox, True, True, 0)

        label1 = Gtk.Label("Automatic Date & Time", xalign=0)
        label2 = Gtk.Label("Requires internet access", xalign=0)
        vbox.pack_start(label1, True, True, 0)
        vbox.pack_start(label2, True, True, 0)

        switch = Gtk.Switch()
        switch.props.valign = Gtk.Align.CENTER
        hbox.pack_start(switch, False, True, 0)

        listbox.add(row)

        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)
        label = Gtk.Label("Enable Automatic Update", xalign=0)
        check = Gtk.CheckButton()
        hbox.pack_start(label, True, True, 0)
        hbox.pack_start(check, False, True, 0)

        listbox.add(row)

        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)
        label = Gtk.Label("Date Format", xalign=0)
        combo = Gtk.ComboBoxText()
        combo.insert(0, "0", "24-hour")
        combo.insert(1, "1", "AM/PM")
        hbox.pack_start(label, True, True, 0)
        hbox.pack_start(combo, False, True, 0)

        listbox.add(row)

        listbox_2 = Gtk.ListBox()
        items = 'This is a sorted ListBox Fail'.split()

        for item in items:
            listbox_2.add(ListBoxRowWithData(item))

        def sort_func(row_1, row_2, data, notify_destroy):
            return row_1.data.lower() > row_2.data.lower()

        def filter_func(row, data, notify_destroy):
            return False if row.data == 'Fail' else True

        listbox_2.set_sort_func(sort_func, None, False)
        listbox_2.set_filter_func(filter_func, None, False)

        listbox_2.connect('row-activated', lambda widget, row: print(row.data))
        
        box_outer.pack_start(listbox_2, True, True, 0)
        listbox_2.show_all()

win = ListBoxWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Stack和StackSwitcher

一个Gtk.Stack是一次只显示一个孩子的容器。 Gtk.Notebook相比,Gtk.Stack不提供用户更改可见子项的方法。 相反,Gtk.StackSwitcher小部件可与Gtk.Stack一起使用以提供此功能。

页面之间的转换可以作为幻灯片或淡入淡出来进行动画制作。 这可以通过Gtk.Stack.set_transition_type()进行控制。 这些动画尊重“gtk-enable-animations”设置。

转换速度可以通过Gtk.Stack.set_transition_duration()进行调整

Gtk.StackSwitcher小部件充当Gtk.Stack的控制器;它显示了一排按钮以在关联的堆栈控件的各个页面之间切换。

所有按钮的内容都来自Gtk.Stack的子属性。

可以将多个Gtk.StackSwitcher小部件与同一个Gtk.Stack小部件相关联。

实施例¶ T0>

_images/stack_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class StackWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Stack Demo")
        self.set_border_width(10)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)

        stack = Gtk.Stack()
        stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        stack.set_transition_duration(1000)
        
        checkbutton = Gtk.CheckButton("Click me!")
        stack.add_titled(checkbutton, "check", "Check Button")
        
        label = Gtk.Label()
        label.set_markup("<big>A fancy label</big>")
        stack.add_titled(label, "label", "A label")

        stack_switcher = Gtk.StackSwitcher()
        stack_switcher.set_stack(stack)
        vbox.pack_start(stack_switcher, True, True, 0)
        vbox.pack_start(stack, True, True, 0)

win = StackWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

HeaderBar ¶ T0>

一个Gtk.HeaderBar类似于一个水平的Gtk.Box,它允许在开始或结束时放置子节点。 另外,它允许显示标题。 即使两边的儿童占用不同数量的空间,标题也会以框的宽度为中心。

由于GTK +现在支持客户端装饰,可以使用Gtk.HeaderBar来代替标题栏(由窗口管理器呈现)。

Gtk.HeaderBar通常位于窗口的顶部,并应包含影响以下内容的常用控件。 他们还提供对窗口控件的访问,包括关闭窗口按钮和窗口菜单。

实施例¶ T0>

_images/headerbar_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio

class HeaderBarWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="HeaderBar Demo")
        self.set_border_width(10)
        self.set_default_size(400, 200)

        hb = Gtk.HeaderBar()
        hb.set_show_close_button(True)
        hb.props.title = "HeaderBar example"
        self.set_titlebar(hb)

        button = Gtk.Button()
        icon = Gio.ThemedIcon(name="mail-send-receive-symbolic")
        image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
        button.add(image)
        hb.pack_end(button)
        
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        Gtk.StyleContext.add_class(box.get_style_context(), "linked")

        button = Gtk.Button()
        button.add(Gtk.Arrow(Gtk.ArrowType.LEFT, Gtk.ShadowType.NONE))
        box.add(button)

        button = Gtk.Button()
        button.add(Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE))
        box.add(button)

        hb.pack_start(box)
        
        self.add(Gtk.TextView())

win = HeaderBarWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

FlowBox ¶ T0>

Gtk.FlowBox是一个容器,它根据其方向依次定位子小部件。

例如,在水平方向上,小部件将从左到右排列,必要时在前一行下开始一个新行。 在这种情况下减小宽度将需要更多的行,因此需要更大的高度。

同样,在垂直方向上,小部件将从上到下排列,并在必要时向右开始新列。 降低高度需要更多的列,因此需要更大的宽度。

可以对Gtk.FlowBox的孩子进行动态排序和过滤。

Although a Gtk.FlowBox must have only Gtk.FlowBoxChild children, you can add any kind of widget to it via Gtk.Container.add(), and a Gtk.FlowBoxChild widget will automatically be inserted between the box and the widget.

实施例¶ T0>

_images/flowbox_example.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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

class FlowBoxWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="FlowBox Demo")
        self.set_border_width(10)
        self.set_default_size(300, 250)

        header = Gtk.HeaderBar(title="Flow Box")
        header.set_subtitle("Sample FlowBox app")
        header.props.show_close_button = True

        self.set_titlebar(header)

        scrolled = Gtk.ScrolledWindow()
        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)

        flowbox = Gtk.FlowBox()
        flowbox.set_valign(Gtk.Align.START)
        flowbox.set_max_children_per_line(30)
        flowbox.set_selection_mode(Gtk.SelectionMode.NONE)

        self.create_flowbox(flowbox)

        scrolled.add(flowbox)

        self.add(scrolled)
        self.show_all()

    def color_swatch_new(self, str_color):
        color = Gdk.color_parse(str_color)

        rgba = Gdk.RGBA.from_color(color)
        button = Gtk.Button()

        area = Gtk.DrawingArea()
        area.set_size_request(24, 24)
        area.override_background_color(0, rgba)

        button.add(area)

        return button

    def create_flowbox(self, flowbox):
        colors = [
        'AliceBlue',
        'AntiqueWhite',
        'AntiqueWhite1',
        'AntiqueWhite2',
        'AntiqueWhite3',
        'AntiqueWhite4',
        'aqua',
        'aquamarine',
        'aquamarine1',
        'aquamarine2',
        'aquamarine3',
        'aquamarine4',
        'azure',
        'azure1',
        'azure2',
        'azure3',
        'azure4',
        'beige',
        'bisque',
        'bisque1',
        'bisque2',
        'bisque3',
        'bisque4',
        'black',
        'BlanchedAlmond',
        'blue',
        'blue1',
        'blue2',
        'blue3',
        'blue4',
        'BlueViolet',
        'brown',
        'brown1',
        'brown2',
        'brown3',
        'brown4',
        'burlywood',
        'burlywood1',
        'burlywood2',
        'burlywood3',
        'burlywood4',
        'CadetBlue',
        'CadetBlue1',
        'CadetBlue2',
        'CadetBlue3',
        'CadetBlue4',
        'chartreuse',
        'chartreuse1',
        'chartreuse2',
        'chartreuse3',
        'chartreuse4',
        'chocolate',
        'chocolate1',
        'chocolate2',
        'chocolate3',
        'chocolate4',
        'coral',
        'coral1',
        'coral2',
        'coral3',
        'coral4'
        ]

        for color in colors:
            button = self.color_swatch_new(color)
            flowbox.add(button)


win = FlowBoxWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

笔记本¶ T0>

Gtk.Notebook小部件是一个Gtk.Container,其孩子是可以使用一个边缘上的标签标签进行切换的页面。

GtkNotebook有许多配置选项。 Among other things, you can choose on which edge the tabs appear (see Gtk.Notebook.set_tab_pos()), whether, if there are too many tabs to fit the notebook should be made bigger or scrolling arrows added (see Gtk.Notebook.set_scrollable(), and whether there will be a popup menu allowing the users to switch pages (see Gtk.Notebook.popup_enable(), Gtk.Notebook.popup_disable()).

实施例¶ T0>

_images/notebook_plain_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class MyWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Simple Notebook Example")
        self.set_border_width(3)

        self.notebook = Gtk.Notebook()
        self.add(self.notebook)

        self.page1 = Gtk.Box()
        self.page1.set_border_width(10)
        self.page1.add(Gtk.Label('Default Page!'))
        self.notebook.append_page(self.page1, Gtk.Label('Plain Title'))

        self.page2 = Gtk.Box()
        self.page2.set_border_width(10)
        self.page2.add(Gtk.Label('A page with an image for a Title.'))
        self.notebook.append_page(
            self.page2,
            Gtk.Image.new_from_icon_name(
                "help-about",
                Gtk.IconSize.MENU
            )
        )

win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

标签¶ T0>

标签是在Windows中放置不可编辑文本的主要方法,例如将标题放在Gtk.Entry小部件旁边。 您可以在构造函数中指定文本,或稍后使用Gtk.Label.set_text()Gtk.Label.set_markup()方法指定文本。

标签的宽度将自动调整。 您可以通过在标签字符串中放置换行符(“\ n”)来生成多行标签。

标签可以通过Gtk.Label.set_selectable()进行选择。 可选标签允许用户将标签内容复制到剪贴板。 只能选择包含有用信息的标签 - 例如错误信息 - 。

标签文本可以使用Gtk.Label.set_justify()方法进行调整。 该小部件还能够进行文字包装,可以使用Gtk.Label.set_line_wrap()激活。

Gtk.Label支持一些简单的格式,例如允许您将某些文本设为粗体,彩色或更大。 您可以使用Pango标记语法[1]Gtk.Label.set_markup()提供一个字符串。 For instance, <b>bold text</b> and <s>strikethrough text</s>. 另外,Gtk.Label支持可点击的超链接。 链接的标记是从HTML中借用的,使用带有href和title属性的a。 GTK +使用类似于它们在Web浏览器中显示的方式呈现链接,并带有带下划线的彩色文本。 标题属性显示为链接上的工具提示。

label.set_markup("Go to <a href=\"http://www.gtk.org\" "
                 "title=\"Our website\">GTK+ website</a> for more")

标签可能包含助记符 助记符在标签中带有下划线字符,用于键盘导航。 通过在函数Gtk.Label.new_with_mnemonic()Gtk.Label.set_text_with_mnemonic() 助记符自动激活标签所在的任何可激活小部件,例如Gtk.Button;如果标签不在助记符的目标窗口小部件中,则必须使用Gtk.Label.set_mnemonic_widget()来告诉标签。

实施例¶ T0>

_images/label_example.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
71
72
73
74
75
76
77
78
79
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class LabelWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Label Example")
        
        hbox = Gtk.Box(spacing=10)
        hbox.set_homogeneous(False)
        vbox_left = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox_left.set_homogeneous(False)
        vbox_right = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        vbox_right.set_homogeneous(False)
        
        hbox.pack_start(vbox_left, True, True, 0)
        hbox.pack_start(vbox_right, True, True, 0)
        
        label = Gtk.Label("This is a normal label")
        vbox_left.pack_start(label, True, True, 0)
        
        label = Gtk.Label()
        label.set_text("This is a left-justified label.\nWith multiple lines.")
        label.set_justify(Gtk.Justification.LEFT)
        vbox_left.pack_start(label, True, True, 0)
        
        label = Gtk.Label(
            "This is a right-justified label.\nWith multiple lines.")
        label.set_justify(Gtk.Justification.RIGHT)
        vbox_left.pack_start(label, True, True, 0)
        
        label = Gtk.Label("This is an example of a line-wrapped label.  It "
                          "should not be taking up the entire             "
                          "width allocated to it, but automatically "
                          "wraps the words to fit.\n"
                          "     It supports multiple paragraphs correctly, "
                          "and  correctly   adds "
                          "many          extra  spaces. ")
        label.set_line_wrap(True)
        vbox_right.pack_start(label, True, True, 0)
        
        label = Gtk.Label("This is an example of a line-wrapped, filled label. "
                          "It should be taking "
                          "up the entire              width allocated to it.  "
                          "Here is a sentence to prove "
                          "my point.  Here is another sentence. "
                          "Here comes the sun, do de do de do.\n"
                          "    This is a new paragraph.\n"
                          "    This is another newer, longer, better "
                          "paragraph.  It is coming to an end, "
                          "unfortunately.")
        label.set_line_wrap(True)
        label.set_justify(Gtk.Justification.FILL)
        vbox_right.pack_start(label, True, True, 0)

        label = Gtk.Label()
        label.set_markup("Text can be <small>small</small>, <big>big</big>, "
                         "<b>bold</b>, <i>italic</i> and even point to "
                         "somewhere in the <a href=\"http://www.gtk.org\" "
                         "title=\"Click to find out more\">internets</a>.")
        label.set_line_wrap(True)
        vbox_left.pack_start(label, True, True, 0)

        label = Gtk.Label.new_with_mnemonic(
            "_Press Alt + P to select button to the right")
        vbox_left.pack_start(label, True, True, 0)
        label.set_selectable(True)

        button = Gtk.Button(label="Click at your own risk")
        label.set_mnemonic_widget(button)
        vbox_right.pack_start(button, True, True, 0)

        self.add(hbox)

window = LabelWindow()        
window.connect("destroy", Gtk.main_quit)
window.show_all()
Gtk.main()
[1]Pango标记语法,http://developer.gnome.org/pango/stable/PangoMarkupFormat.html

输入框¶ T0>

输入小部件允许用户输入文本。 您可以使用Gtk.Entry.set_text()方法更改内容,并使用Gtk.Entry.get_text()方法读取当前内容。 您还可以通过调用Gtk.Entry.set_max_length()来限制Entry可以执行的字符数。

偶尔你可能想让一个Entry小部件只读。 这可以通过将False传递给Gtk.Entry.set_editable()方法来完成。

输入小部件也可以用来从用户检索密码。 通常的做法是将输入的字符隐藏到条目中,以防止向第三方泄露密码。 False调用Gtk.Entry.set_visibility()会导致文本被隐藏。

Gtk.Entry能够显示文本后面的进度或活动信息。 这与Gtk.ProgressBar小部件类似,并且通常在Web浏览器中可以找到,表明已完成多少页下载。 要使条目显示此类信息,请使用Gtk.Entry.set_progress_fraction()Gtk.Entry.set_progress_pulse_step()Gtk.Entry.progress_pulse()

另外,Entry可以在条目的任一侧显示图标。 这些图标可以通过点击激活,可以设置为拖动源,并可以有工具提示。 要添加图标,请使用Gtk.Entry.set_icon_from_icon_name()或其他各种通过图标名称,pixbuf或图标主题设置图标的功能之一。 要在图标上设置工具提示,请使用Gtk.Entry.set_icon_tooltip_text()或相应的标记功能。

实施例¶ T0>

_images/entry_example.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
71
72
73
74
75
76
77
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

class EntryWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Entry Demo")
        self.set_size_request(200, 100)

        self.timeout_id = None

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)

        self.entry = Gtk.Entry()
        self.entry.set_text("Hello World")
        vbox.pack_start(self.entry, True, True, 0)

        hbox = Gtk.Box(spacing=6)
        vbox.pack_start(hbox, True, True, 0)

        self.check_editable = Gtk.CheckButton("Editable")
        self.check_editable.connect("toggled", self.on_editable_toggled)
        self.check_editable.set_active(True)
        hbox.pack_start(self.check_editable, True, True, 0)

        self.check_visible = Gtk.CheckButton("Visible")
        self.check_visible.connect("toggled", self.on_visible_toggled)
        self.check_visible.set_active(True)
        hbox.pack_start(self.check_visible, True, True, 0)

        self.pulse = Gtk.CheckButton("Pulse")
        self.pulse.connect("toggled", self.on_pulse_toggled)
        self.pulse.set_active(False)
        hbox.pack_start(self.pulse, True, True, 0)

        self.icon = Gtk.CheckButton("Icon")
        self.icon.connect("toggled", self.on_icon_toggled)
        self.icon.set_active(False)
        hbox.pack_start(self.icon, True, True, 0)

    def on_editable_toggled(self, button):
        value = button.get_active()
        self.entry.set_editable(value)

    def on_visible_toggled(self, button):
        value = button.get_active()
        self.entry.set_visibility(value)

    def on_pulse_toggled(self, button):
        if button.get_active():
            self.entry.set_progress_pulse_step(0.2)
            # Call self.do_pulse every 100 ms
            self.timeout_id = GObject.timeout_add(100, self.do_pulse, None)
        else:
            # Don't call self.do_pulse anymore
            GObject.source_remove(self.timeout_id)
            self.timeout_id = None
            self.entry.set_progress_pulse_step(0)

    def do_pulse(self, user_data):
        self.entry.progress_pulse()
        return True

    def on_icon_toggled(self, button):
        if button.get_active():
            icon_name = "system-search-symbolic"
        else:
            icon_name = None
        self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,
            icon_name)

win = EntryWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

按钮

按钮¶ T0>

Button小部件是另一个常用的小部件。 它通常用于附加按下按钮时调用的函数。

Gtk.Button小部件可以包含任何有效的子小部件。 这是它可以容纳大多数其他标准的Gtk.Widget 最常用的孩子是Gtk.Label

通常,您要连接到按钮被按下并释放时发出的按钮的“单击”信号。

实施例¶ T0>

_images/button_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class ButtonWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Button Demo")
        self.set_border_width(10)

        hbox = Gtk.Box(spacing=6)
        self.add(hbox)

        button = Gtk.Button.new_with_label("Click Me")
        button.connect("clicked", self.on_click_me_clicked)
        hbox.pack_start(button, True, True, 0)

        button = Gtk.Button.new_with_mnemonic("_Open")
        button.connect("clicked", self.on_open_clicked)
        hbox.pack_start(button, True, True, 0)

        button = Gtk.Button.new_with_mnemonic("_Close")
        button.connect("clicked", self.on_close_clicked)
        hbox.pack_start(button, True, True, 0)

    def on_click_me_clicked(self, button):
        print("\"Click me\" button was clicked")

    def on_open_clicked(self, button):
        print("\"Open\" button was clicked")

    def on_close_clicked(self, button):
        print("Closing application")
        Gtk.main_quit()

win = ButtonWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

切换按钮¶ T0>

一个Gtk.ToggleButton与正常的Gtk.Button非常相似,但是当点击时,它们会保持激活或按下状态,直到再次点击。 当按钮的状态改变时,发出“切换”信号。

要检索Gtk.ToggleButton的状态,可以使用Gtk.ToggleButton.get_active()方法。 如果按钮是“down”,则返回True 您还可以使用Gtk.ToggleButton.set_active()设置切换按钮的状态。 请注意,如果您这样做,并且状态实际发生变化,则会导致发出“切换”信号。

实施例¶ T0>

_images/togglebutton_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class ToggleButtonWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="ToggleButton Demo")
        self.set_border_width(10)

        hbox = Gtk.Box(spacing=6)
        self.add(hbox)

        button = Gtk.ToggleButton("Button 1")
        button.connect("toggled", self.on_button_toggled, "1")
        hbox.pack_start(button, True, True, 0)

        button = Gtk.ToggleButton("B_utton 2", use_underline=True)
        button.set_active(True)
        button.connect("toggled", self.on_button_toggled, "2")
        hbox.pack_start(button, True, True, 0)

    def on_button_toggled(self, button, name):
        if button.get_active():
            state = "on"
        else:
            state = "off"
        print("Button", name, "was turned", state)

win = ToggleButtonWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

CheckButton ¶ T0>

Gtk.CheckButton继承自Gtk.ToggleButton 两者之间唯一真正的区别是Gtk.CheckButton的外观。 Gtk.CheckButton将一个离散的Gtk.ToggleButton放置在一个小部件旁边(通常是Gtk.Label)。 The “toggled” signal, Gtk.ToggleButton.set_active() and Gtk.ToggleButton.get_active() are inherited.

单选按钮¶ T0>

与复选框类似,单选按钮也从Gtk.ToggleButton继承,但这些按组分组,每次只能选择一个组中的一个Gtk.RadioButton 因此,一个Gtk.RadioButton是给用户许多选择的一种选择。

可以用静态方法之一创建单选按钮Gtk.RadioButton.new_from_widget()Gtk.RadioButton.new_with_label_from_widget()Gtk.RadioButton.new_with_mnemonic_from_widget() 组中的第一个单选按钮将作为参数传递None 在随后的调用中,您希望添加此按钮的组应该作为参数传递。

首次运行时,组中的第一个单选按钮将处于活动状态。 这可以通过使用True作为第一个参数来调用Gtk.ToggleButton.set_active()来改变。

通过调用Gtk.RadioButton.join_group(),可以在创建后更改Gtk.RadioButton的窗口小部件组。

实施例¶ T0>

_images/radiobutton_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class RadioButtonWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="RadioButton Demo")
        self.set_border_width(10)

        hbox = Gtk.Box(spacing=6)
        self.add(hbox)

        button1 = Gtk.RadioButton.new_with_label_from_widget(None, "Button 1")
        button1.connect("toggled", self.on_button_toggled, "1")
        hbox.pack_start(button1, False, False, 0)

        button2 = Gtk.RadioButton.new_from_widget(button1)
        button2.set_label("Button 2")
        button2.connect("toggled", self.on_button_toggled, "2")
        hbox.pack_start(button2, False, False, 0)

        button3 = Gtk.RadioButton.new_with_mnemonic_from_widget(button1,
            "B_utton 3")
        button3.connect("toggled", self.on_button_toggled, "3")
        hbox.pack_start(button3, False, False, 0)

    def on_button_toggled(self, button, name):
        if button.get_active():
            state = "on"
        else:
            state = "off"
        print("Button", name, "was turned", state)

win = RadioButtonWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

LinkBut​​ton的¶ T0>

一个Gtk.LinkButton是一个带有超链接的Gtk.Button,类似于网页浏览器使用的超链接,当点击时触发一个动作。 显示资源的快速链接很有用。

绑定到Gtk.LinkButton的URI可以使用Gtk.LinkButton.set_uri()来专门设置,并使用Gtk.LinkButton.get_uri()

实施例¶ T0>

_images/linkbutton_example.png
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class LinkButtonWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="LinkButton Demo")
        self.set_border_width(10)

        button = Gtk.LinkButton("http://www.gtk.org", "Visit GTK+ Homepage")
        self.add(button)

win = LinkButtonWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

数值调节钮¶ T0>

Gtk.SpinButton是允许用户设置某个属性的值的理想方法。 Rather than having to directly type a number into a Gtk.Entry, Gtk.SpinButton allows the user to click on one of two arrows to increment or decrement the displayed value. 值仍然可以输入,并且可以检查以确保它在给定范围内的奖励。 Gtk.SpinButton的主要属性通过Gtk.Adjustment进行设置。

要更改Gtk.SpinButton显示的值,请使用Gtk.SpinButton.set_value() 输入的值可以是整数或浮点数,具体取决于您的要求,分别使用Gtk.SpinButton.get_value()Gtk.SpinButton.get_value_as_int()

当您允许在旋转按钮中显示浮点值时,您可能希望通过调用Gtk.SpinButton.set_digits()来调整显示的小数位数。

默认情况下,Gtk.SpinButton接受文本数据。 如果您希望仅限于数值,请以True作为参数来调用Gtk.SpinButton.set_numeric()

我们也可以调整Gtk.SpinButton的更新策略。 这里有两种选择。默认情况下,即使输入的数据无效,旋转按钮也会更新值。 或者,我们可以通过调用Gtk.SpinButton.set_update_policy()将策略设置为仅当输入的值有效时更新。

实施例¶ T0>

_images/spinbutton_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class SpinButtonWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="SpinButton Demo")
        self.set_border_width(10)

        hbox = Gtk.Box(spacing=6)
        self.add(hbox)

        adjustment = Gtk.Adjustment(0, 0, 100, 1, 10, 0)
        self.spinbutton = Gtk.SpinButton()
        self.spinbutton.set_adjustment(adjustment)
        hbox.pack_start(self.spinbutton, False, False, 0)

        check_numeric = Gtk.CheckButton("Numeric")
        check_numeric.connect("toggled", self.on_numeric_toggled)
        hbox.pack_start(check_numeric, False, False, 0)

        check_ifvalid = Gtk.CheckButton("If Valid")
        check_ifvalid.connect("toggled", self.on_ifvalid_toggled)
        hbox.pack_start(check_ifvalid, False, False, 0)

    def on_numeric_toggled(self, button):
        self.spinbutton.set_numeric(button.get_active())

    def on_ifvalid_toggled(self, button):
        if button.get_active():
            policy = Gtk.SpinButtonUpdatePolicy.IF_VALID
        else:
            policy = Gtk.SpinButtonUpdatePolicy.ALWAYS
        self.spinbutton.set_update_policy(policy)

win = SpinButtonWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

开关¶ T0>

Gtk.Switch是一个具有两种状态的小部件:打开或关闭。 用户可以通过单击空白区域或通过拖动手柄来控制应激活哪个状态。

您不应该使用作为动作信号的Gtk.Switch上的“激活”信号,然后将其发送给开关进行动画播放。 应用程序不应连接到此信号,但使用“notify :: active”信号,请参阅下面的示例。

实施例¶ T0>

_images/switch_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class SwitcherWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Switch Demo")
        self.set_border_width(10)

        hbox = Gtk.Box(spacing=6)
        self.add(hbox)

        switch = Gtk.Switch()
        switch.connect("notify::active", self.on_switch_activated)
        switch.set_active(False)
        hbox.pack_start(switch, True, True, 0)

        switch = Gtk.Switch()
        switch.connect("notify::active", self.on_switch_activated)
        switch.set_active(True)
        hbox.pack_start(switch, True, True, 0)

    def on_switch_activated(self, switch, gparam):
        if switch.get_active():
            state = "on"
        else:
            state = "off"
        print("Switch was turned", state)

win = SwitcherWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

进度¶ T0>

Gtk.ProgressBar通常用于显示长时间运行操作的进度。 它提供了一个处理正在进行的视觉线索。 Gtk.ProgressBar可以用于两种不同的模式:百分比模式活动模式

当应用程序可以确定需要进行多少工作(例如,从文件中读取固定数量的字节)并且可以监视其进度时,它可以使用百分比模式中的Gtk.ProgressBar ,用户看到一个不断增长的栏,显示已完成工作的百分比。 在这种模式下,应用程序需要定期调用Gtk.ProgressBar.set_fraction()来更新进度条,并在0和1之间传递一个浮点数以提供新的百分比值。

当应用程序没有准确的方法知道要完成的工作量时,它可以使用活动模式,它显示在进度区域内来回移动块的活动。 在这种模式下,应用程序需要定期调用Gtk.ProgressBar.pulse()来更新进度条。 您还可以使用Gtk.ProgressBar.set_pulse_step()方法选择步长。

默认情况下,Gtk.ProgressBar是水平的并且从左到右,但是您可以通过使用Gtk.ProgressBar.set_orientation()方法将其更改为垂直进度条。 可以使用Gtk.ProgressBar.set_inverted()完成改变进度条增长的方向。 Gtk.ProgressBar也可以包含通过调用Gtk.ProgressBar.set_text()Gtk.ProgressBar.set_show_text()来设置的文本。

实施例¶ T0>

_images/progressbar_example.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
71
72
73
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

class ProgressBarWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="ProgressBar Demo")
        self.set_border_width(10)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)

        self.progressbar = Gtk.ProgressBar()
        vbox.pack_start(self.progressbar, True, True, 0)

        button = Gtk.CheckButton("Show text")
        button.connect("toggled", self.on_show_text_toggled)
        vbox.pack_start(button, True, True, 0)

        button = Gtk.CheckButton("Activity mode")
        button.connect("toggled", self.on_activity_mode_toggled)
        vbox.pack_start(button, True, True, 0)

        button = Gtk.CheckButton("Right to Left")
        button.connect("toggled", self.on_right_to_left_toggled)
        vbox.pack_start(button, True, True, 0)

        self.timeout_id = GObject.timeout_add(50, self.on_timeout, None)
        self.activity_mode = False

    def on_show_text_toggled(self, button):
        show_text = button.get_active()
        if show_text:
            text = "some text"
        else:
            text = None
        self.progressbar.set_text(text)
        self.progressbar.set_show_text(show_text)

    def on_activity_mode_toggled(self, button):
        self.activity_mode = button.get_active()
        if self.activity_mode:
            self.progressbar.pulse()
        else:
            self.progressbar.set_fraction(0.0)

    def on_right_to_left_toggled(self, button):
        value = button.get_active()
        self.progressbar.set_inverted(value)

    def on_timeout(self, user_data):
        """
        Update value on the progress bar
        """
        if self.activity_mode:
            self.progressbar.pulse()
        else:
            new_value = self.progressbar.get_fraction() + 0.01

            if new_value > 1:
                new_value = 0

            self.progressbar.set_fraction(new_value)

        # As this is a timeout function, return True so that it
        # continues to get called
        return True

win = ProgressBarWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

喷丝¶ T0>

Gtk.Spinner显示一个图标大小的旋转动画。 它通常用于替代GtkProgressBar来显示不确定的活动,而不是实际的进度。

要开始动画,请使用Gtk.Spinner.start(),以停止使用Gtk.Spinner.stop()

实施例¶ T0>

_images/spinner_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class SpinnerAnimation(Gtk.Window):

    def __init__(self):

        Gtk.Window.__init__(self, title="Spinner")
        self.set_border_width(3)
        self.connect("destroy", Gtk.main_quit)

        self.button = Gtk.ToggleButton("Start Spinning")
        self.button.connect("toggled", self.on_button_toggled)
        self.button.set_active(False)

        self.spinner = Gtk.Spinner()

        self.table = Gtk.Table(3, 2, True)
        self.table.attach(self.button, 0, 2, 0, 1)
        self.table.attach(self.spinner, 0, 2, 2, 3)

        self.add(self.table)
        self.show_all()

    def on_button_toggled(self, button):

        if button.get_active():
            self.spinner.start()
            self.button.set_label("Stop Spinning")

        else:
            self.spinner.stop()
            self.button.set_label("Start Spinning")


myspinner = SpinnerAnimation()

Gtk.main()

扩展示例

一个使用超时函数来启动和停止旋转动画的扩展示例。 定期调用on_timeout()函数,直到它返回False,此时超时会自动销毁,函数将不会再次被调用。

实施例¶ T0>

_images/spinner_ext_example.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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

class SpinnerWindow(Gtk.Window):

    def __init__(self, *args, **kwargs):
        Gtk.Window.__init__(self, title="Spinner Demo")
        self.set_border_width(10)

        mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(mainBox)

        self.spinner = Gtk.Spinner()
        mainBox.pack_start(self.spinner, True, True, 0)

        self.label = Gtk.Label()
        mainBox.pack_start(self.label, True, True, 0)

        self.entry = Gtk.Entry()
        self.entry.set_text('10')
        mainBox.pack_start(self.entry, True, True, 0)

        self.buttonStart = Gtk.Button("Start timer")
        self.buttonStart.connect("clicked", self.on_buttonStart_clicked)
        mainBox.pack_start(self.buttonStart, True, True, 0)

        self.buttonStop = Gtk.Button("Stop timer")
        self.buttonStop.set_sensitive(False)
        self.buttonStop.connect("clicked", self.on_buttonStop_clicked)
        mainBox.pack_start(self.buttonStop, True, True, 0)

        self.timeout_id = None
        self.connect("destroy", self.on_SpinnerWindow_destroy)

    def on_buttonStart_clicked(self, widget, *args):
        """ Handles "clicked" event of buttonStart. """
        self.start_timer()

    def on_buttonStop_clicked(self, widget, *args):
        """ Handles "clicked" event of buttonStop. """
        self.stop_timer('Stopped from button')

    def on_SpinnerWindow_destroy(self, widget, *args):
        """ Handles destroy event of main window. """
        # ensure the timeout function is stopped
        if self.timeout_id:
            GObject.source_remove(self.timeout_id)
            self.timeout_id = None
        Gtk.main_quit()

    def on_timeout(self, *args, **kwargs):
        """ A timeout function.

        Return True to stop it.
        This is not a precise timer since next timeout
        is recalculated based on the current time."""
        self.counter -= 1
        if self.counter <= 0:
            self.stop_timer('Reached time out')
            return False
        self.label.set_label('Remaining: ' + str(int(self.counter / 4)))
        return True

    def start_timer(self):
        """ Start the timer. """
        self.buttonStart.set_sensitive(False)
        self.buttonStop.set_sensitive(True)
        # time out will check every 250 miliseconds (1/4 of a second)
        self.counter = 4 * int(self.entry.get_text())
        self.label.set_label('Remaining: ' + str(int(self.counter / 4)))
        self.spinner.start()
        self.timeout_id = GObject.timeout_add(250, self.on_timeout, None)

    def stop_timer(self, alabeltext):
        """ Stop the timer. """
        if self.timeout_id:
            GObject.source_remove(self.timeout_id)
            self.timeout_id = None
        self.spinner.stop()
        self.buttonStart.set_sensitive(True)
        self.buttonStop.set_sensitive(False)
        self.label.set_label(alabeltext)

win = SpinnerWindow()
win.show_all()
Gtk.main()

树和列表小部件

一个Gtk.TreeView及其关联的小部件是显示数据的一种非常强大的方式。 它们与Gtk.ListStoreGtk.TreeStore结合使用,并以多种方式提供显示和操作数据的方式,其中包括:

  • 数据添加,删除或编辑时自动更新
  • 拖放支持
  • 排序数据
  • 嵌入小部件,如复选框,进度条等
  • 可重新调整和可调整大小的列
  • 过滤数据

凭借Gtk.TreeView的强大功能和灵活性来处理复杂性。 由于需要的方法数量多,初学者开发人员通常难以正确地使用它。

模型

每个Gtk.TreeView都有一个关联的Gtk.TreeModel,其中包含TreeView显示的数据。 每个Gtk.TreeModel可以被多个Gtk.TreeView使用。 例如,这可以同时以两种不同的方式显示和编辑相同的底层数据。 或者2个视图可能会显示来自相同模型数据的不同列,与2个SQL查询(或“视图”)可能显示来自同一数据库表的不同字段的方式相同。

虽然理论上可以实现自己的模型,但通常会使用Gtk.ListStoreGtk.TreeStore模型类。 Gtk.ListStore包含简单的数据行,并且每行都没有孩子,而Gtk.TreeStore包含数据行,每行可能有子行。

在构建模型时,您必须指定模型中每个列的数据类型。

store = Gtk.ListStore(str, str, float)

这将创建一个包含三列,两个字符串列和一个浮点列的列表存储。

根据创建的模型类型,使用Gtk.ListStore.append()Gtk.TreeStore.append()完成向模型添加数据。

treeiter = store.append(["The Art of Computer Programming",
                         "Donald E. Knuth", 25.46])

这两种方法都返回一个Gtk.TreeIter实例,它指向新插入行的位置。 您可以通过调用Gtk.TreeModel.get_iter()来检索Gtk.TreeIter

插入数据后,您可以使用树iter和列索引检索或修改数据。

print(store[treeiter][2]) # Prints value of third column
store[treeiter][2] = 42.15

与Python内置的list对象一样,您可以使用len()获取行数并使用切片检索或设置值。

# Print number of rows
print(len(store))
# Print all but first column
print(store[treeiter][1:])
# Print last column
print(store[treeiter][-1])
# Set last two columns
store[treeiter][1:] = ["Donald Ervin Knuth", 41.99]

迭代树模型的所有行也非常简单。

for row in store:
    # Print values of all columns
    print(row[:])

请记住,如果您使用Gtk.TreeStore,上面的代码将只遍历顶层的行,而不是遍历节点的子节点。 要迭代所有行及其子元素,请使用print_tree_store函数。

def print_tree_store(store):
    rootiter = store.get_iter_first()
    print_rows(store, rootiter, "")

def print_rows(store, treeiter, indent):
    while treeiter is not None:
        print(indent + str(store[treeiter][:]))
        if store.iter_has_child(treeiter):
            childiter = store.iter_children(treeiter)
            print_rows(store, childiter, indent + "\t")
        treeiter = store.iter_next(treeiter)

除了使用上面提到的类列表方法访问存储在Gtk.TreeModel中的值之外,还可以使用Gtk.TreeIterGtk.TreePath实例。 两者都引用树模型中的特定行。 通过调用Gtk.TreeModel.get_iter(),可以将路径转换为迭代器。 由于Gtk.ListStore仅包含一个级别,即节点没有任何子节点,所以路径本质上就是要访问的行的索引。

# Get path pointing to 6th row in list store
path = Gtk.TreePath(5)
treeiter = liststore.get_iter(path)
# Get value at 2nd column
value = liststore.get_value(treeiter, 1)

Gtk.TreeStore的情况下,路径是索引或字符串的列表。 字符串形式是由冒号分隔的数字列表。 每个数字指的是该级别的偏移量。 因此,路径“0”指的是根节点,路径“2:4”指的是第三个节点的第五个子节点。

# Get path pointing to 5th child of 3rd row in tree store
path = Gtk.TreePath([2, 4])
treeiter = treestore.get_iter(path)
# Get value at 2nd column
value = treestore.get_value(treeiter, 1)

Instances of Gtk.TreePath can be accessed like lists, i.e. len(treepath) returns the depth of the item treepath is pointing to, and treepath[i] returns the child’s index on the i-th level.

视图

虽然有几种不同的模型可供选择,但只有一个视图小部件需要处理。 它适用于列表或树存储。 设置Gtk.TreeView不是一件困难的事情。 它需要一个Gtk.TreeModel来通过传递给Gtk.TreeView构造函数或调用Gtk.TreeView.set_model()

tree = Gtk.TreeView(store)

一旦Gtk.TreeView小部件有一个模型,它将需要知道如何显示模型。 它使用列和单元格渲染器来完成此操作。

单元渲染器用于以某种方式在树模型中绘制数据。 GTK +有许多单元格渲染器,例如Gtk.CellRendererTextGtk.CellRendererPixbufGtk.CellRendererToggle 另外,自己编写自定义渲染器相对比较容易。

Gtk.TreeViewColumnGtk.TreeView用来组织树视图中的垂直列的对象。 它需要知道要为用户标记的列的名称,要使用的单元格渲染器的类型以及要从模型中为给定行检索哪些数据。

renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Title", renderer, text=0)
tree.append_column(column)

要在视图列中呈现多个模型列,您需要创建一个Gtk.TreeViewColumn实例并使用Gtk.TreeViewColumn.pack_start()将模型列添加到它。

column = Gtk.TreeViewColumn("Title and Author")

title = Gtk.CellRendererText()
author = Gtk.CellRendererText()

column.pack_start(title, True)
column.pack_start(author, True)

column.add_attribute(title, "text", 0)
column.add_attribute(author, "text", 1)

tree.append_column(column)

选择

大多数应用程序不仅需要处理显示数据,还需要接收来自用户的输入事件。 为此,只需获取对选择对象的引用并连接到“已更改”信号。

select = tree.get_selection()
select.connect("changed", on_tree_selection_changed)

然后检索所选行的数据:

def on_tree_selection_changed(selection):
    model, treeiter = selection.get_selected()
    if treeiter is not None:
        print("You selected", model[treeiter][0])

您可以通过调用Gtk.TreeSelection.set_mode()来控制允许哪些选择。 如果选择模式设置为Gtk.SelectionMode.MULTIPLE,那么Gtk.TreeSelection.get_selected()不起作用,请使用Gtk.TreeSelection.get_selected_rows()

排序¶ T0>

排序是树视图的一个重要特性,并且由标准树模型(Gtk.TreeStoreGtk.ListStore)支持,它实现Gtk.TreeSortable

点击列进行排序

通过调用Gtk.TreeViewColumn.set_sort_column_id()Gtk.TreeView的列可以很容易地进行排序。 之后,可以通过单击其标题对列进行排序。

首先,我们需要一个简单的Gtk.TreeView和一个Gtk.ListStore作为模型。

model = Gtk.ListStore(str)
model.append(["Benjamin"])
model.append(["Charles"])
model.append(["alfred"])
model.append(["Alfred"])
model.append(["David"])
model.append(["charles"])
model.append(["david"])
model.append(["benjamin"])

treeView = Gtk.TreeView(model)

cellRenderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Title", renderer, text=0)

下一步是启用排序。 请注意,column_id(示例中的0)引用了模型的列,而不引用到TreeView的列。

column.set_sort_column_id(0)

设置自定义排序功能

也可以设置自定义比较函数以改变排序行为。 作为一个例子,我们将创建一个比较函数来区分大小写。 在上面的例子中,排序后的列表如下所示:

alfred
Alfred
benjamin
Benjamin
charles
Charles
david
David

区分大小写的排序列表如下所示:

Alfred
Benjamin
Charles
David
alfred
benjamin
charles
david

首先需要比较功能。 这个函数获取两行,如果第一个应该在第二个之前出现,则必须返回一个负整数,如果它们相等,则返回零;如果第二个应该在第二个之前出现,则返回一个正整数。

def compare(model, row1, row2, user_data):
    sort_column, _ = model.get_sort_column_id()
    value1 = model.get_value(row1, sort_column)
    value2 = model.get_value(row2, sort_column)
    if value1 < value2:
        return -1
    elif value1 == value2:
        return 0
    else:
        return 1

然后排序函数必须由Gtk.TreeSortable.set_sort_func()设置。

model.set_sort_func(0, compare, None)

过滤¶ T0>

与排序不同,过滤不是由我们以前看到的两个模型处理的,而是由Gtk.TreeModelFilter类处理的。 This class, like Gtk.TreeStore and Gtk.ListStore, is a Gtk.TreeModel. 它充当“真实”模型(Gtk.TreeStoreGtk.ListStore)之间的层,将一些元素隐藏到视图中。 实际上,它为Gtk.TreeView提供了底层模型的一个子集。 Gtk.TreeModelFilter的实例可以堆叠到另一个上,以在同一模型上使用多个过滤器(与在SQL请求中使用“AND”子句的方式相同)。 它们也可以与Gtk.TreeModelSort实例链接。

You can create a new instance of a Gtk.TreeModelFilter and give it a model to filter, but the easiest way is to spawn it directly from the filtered model, using the Gtk.TreeModel.filter_new() method.

filter = model.filter_new()

与排序函数的工作方式相同,Gtk.TreeModelFilter需要一个“可见性”函数,如果给定来自底层模型的行,它将返回一个布尔值,指示是否应该过滤掉该行或不。 它由Gtk.TreeModelFilter.set_visible_func()设置:

filter.set_visible_func(filter_func, data=None)

Let’s look at a full example which uses the whole Gtk.ListStore - Gtk.TreeModelFilter - Gtk.TreeModelFilter - Gtk.TreeView stack.

_images/treeview_filter_example.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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

#list of tuples for each software, containing the software name, initial release, and main programming languages used
software_list = [("Firefox", 2002,  "C++"),
                 ("Eclipse", 2004, "Java" ),
                 ("Pitivi", 2004, "Python"),
                 ("Netbeans", 1996, "Java"),
                 ("Chrome", 2008, "C++"),
                 ("Filezilla", 2001, "C++"),
                 ("Bazaar", 2005, "Python"),
                 ("Git", 2005, "C"),
                 ("Linux Kernel", 1991, "C"),
                 ("GCC", 1987, "C"),
                 ("Frostwire", 2004, "Java")]

class TreeViewFilterWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Treeview Filter Demo")
        self.set_border_width(10)

        #Setting up the self.grid in which the elements are to be positionned
        self.grid = Gtk.Grid()
        self.grid.set_column_homogeneous(True)
        self.grid.set_row_homogeneous(True)
        self.add(self.grid)

        #Creating the ListStore model
        self.software_liststore = Gtk.ListStore(str, int, str)
        for software_ref in software_list:
            self.software_liststore.append(list(software_ref))
        self.current_filter_language = None

        #Creating the filter, feeding it with the liststore model
        self.language_filter = self.software_liststore.filter_new()
        #setting the filter function, note that we're not using the
        self.language_filter.set_visible_func(self.language_filter_func)

        #creating the treeview, making it use the filter as a model, and adding the columns
        self.treeview = Gtk.TreeView.new_with_model(self.language_filter)
        for i, column_title in enumerate(["Software", "Release Year", "Programming Language"]):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(column_title, renderer, text=i)
            self.treeview.append_column(column)

        #creating buttons to filter by programming language, and setting up their events
        self.buttons = list()
        for prog_language in ["Java", "C", "C++", "Python", "None"]:
            button = Gtk.Button(prog_language)
            self.buttons.append(button)
            button.connect("clicked", self.on_selection_button_clicked)

        #setting up the layout, putting the treeview in a scrollwindow, and the buttons in a row
        self.scrollable_treelist = Gtk.ScrolledWindow()
        self.scrollable_treelist.set_vexpand(True)
        self.grid.attach(self.scrollable_treelist, 0, 0, 8, 10)
        self.grid.attach_next_to(self.buttons[0], self.scrollable_treelist, Gtk.PositionType.BOTTOM, 1, 1)
        for i, button in enumerate(self.buttons[1:]):
            self.grid.attach_next_to(button, self.buttons[i], Gtk.PositionType.RIGHT, 1, 1)
        self.scrollable_treelist.add(self.treeview)

        self.show_all()

    def language_filter_func(self, model, iter, data):
        """Tests if the language in the row is the one in the filter"""
        if self.current_filter_language is None or self.current_filter_language == "None":
            return True
        else:
            return model[iter][2] == self.current_filter_language

    def on_selection_button_clicked(self, widget):
        """Called on any of the button clicks"""
        #we set the current language filter to the button's label
        self.current_filter_language = widget.get_label()
        print("%s language selected!" % self.current_filter_language)
        #we update the filter, which updates in turn the view
        self.language_filter.refilter()


win = TreeViewFilterWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

CellRenderers ¶ T0>

Gtk.CellRenderer widgets are used to display information within widgets such as the Gtk.TreeView or Gtk.ComboBox. 他们与关联的小部件密切合作,功能非常强大,有许多配置选项可用于以不同方式显示大量数据。 有七个Gtk.CellRenderer小部件可用于不同的目的:

CellRendererText ¶ T0>

Gtk.CellRendererText使用其属性提供的字体,颜色和样式信息在单元格中呈现给定的文本。 如果文本太长并且“ellipsize”属性允许,文本将被省略。

默认情况下,Gtk.CellRendererText小部件中的文本不可编辑。 这可以通过将“可编辑”属性的值设置为True来更改:

cell.set_property("editable", True)

然后,您可以连接到“编辑”信号并相应地更新您的Gtk.TreeModel

实施例¶ T0>

_images/cellrenderertext_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class CellRendererTextWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererText Example")

        self.set_default_size(200, 200)

        self.liststore = Gtk.ListStore(str, str)
        self.liststore.append(["Fedora", "http://fedoraproject.org/"])
        self.liststore.append(["Slackware", "http://www.slackware.com/"])
        self.liststore.append(["Sidux", "http://sidux.com/"])

        treeview = Gtk.TreeView(model=self.liststore)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
        treeview.append_column(column_text)

        renderer_editabletext = Gtk.CellRendererText()
        renderer_editabletext.set_property("editable", True)

        column_editabletext = Gtk.TreeViewColumn("Editable Text",
            renderer_editabletext, text=1)
        treeview.append_column(column_editabletext)

        renderer_editabletext.connect("edited", self.text_edited)

        self.add(treeview)

    def text_edited(self, widget, path, text):
        self.liststore[path][1] = text

win = CellRendererTextWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

CellRendererToggle ¶ T0>

Gtk.CellRendererToggle在单元格中呈现切换按钮。 该按钮被绘制为无线电或检查按钮,具体取决于“无线电”属性。 激活后,它会发出“切换”信号。

由于Gtk.CellRendererToggle可以有两种状态,即活动状态和非活动状态,因此最有可能希望将单元格渲染器上的“活动”属性绑定到模型中的布尔值,从而导致检查按钮以反映模型的状态。

实施例¶ T0>

_images/cellrenderertoggle_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class CellRendererToggleWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererToggle Example")

        self.set_default_size(200, 200)

        self.liststore = Gtk.ListStore(str, bool, bool)
        self.liststore.append(["Debian", False, True])
        self.liststore.append(["OpenSuse", True, False])
        self.liststore.append(["Fedora", False, False])

        treeview = Gtk.TreeView(model=self.liststore)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
        treeview.append_column(column_text)

        renderer_toggle = Gtk.CellRendererToggle()
        renderer_toggle.connect("toggled", self.on_cell_toggled)

        column_toggle = Gtk.TreeViewColumn("Toggle", renderer_toggle, active=1)
        treeview.append_column(column_toggle)

        renderer_radio = Gtk.CellRendererToggle()
        renderer_radio.set_radio(True)
        renderer_radio.connect("toggled", self.on_cell_radio_toggled)

        column_radio = Gtk.TreeViewColumn("Radio", renderer_radio, active=2)
        treeview.append_column(column_radio)

        self.add(treeview)

    def on_cell_toggled(self, widget, path):
        self.liststore[path][1] = not self.liststore[path][1]

    def on_cell_radio_toggled(self, widget, path):
        selected_path = Gtk.TreePath(path)
        for row in self.liststore:
            row[2] = (row.path == selected_path)

win = CellRendererToggleWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

CellRendererPixbuf ¶ T0>

一个Gtk.CellRendererPixbuf可以用来渲染单元格中的图像。 它允许呈现给定的Gdk.Pixbuf(通过“pixbuf”属性设置)或命名图标(通过“icon-name”属性设置)。

实施例¶ T0>

_images/cellrendererpixbuf_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class CellRendererPixbufWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererPixbuf Example")

        self.set_default_size(200, 200)

        self.liststore = Gtk.ListStore(str, str)
        self.liststore.append(["New", "document-new"])
        self.liststore.append(["Open", "document-open"])
        self.liststore.append(["Save", "document-save"])

        treeview = Gtk.TreeView(model=self.liststore)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
        treeview.append_column(column_text)

        renderer_pixbuf = Gtk.CellRendererPixbuf()

        column_pixbuf = Gtk.TreeViewColumn("Image", renderer_pixbuf, icon_name=1)
        treeview.append_column(column_pixbuf)

        self.add(treeview)

win = CellRendererPixbufWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

CellRendererCombo ¶ T0>

Gtk.CellRendererCombo将文本呈现在单元格中,例如从其派生的Gtk.CellRendererText 但是后者提供了一个简单的条目来编辑文本,Gtk.CellRendererCombo提供了一个Gtk.ComboBox小部件来编辑文本。 要显示在组合框中的值取自“model”属性中指定的Gtk.TreeModel

组合单元格渲染器负责将文本单元格渲染器添加到组合框,并将其设置为显示由其“text-column”属性指定的列。

一个Gtk.CellRendererCombo可以在两种模式下运行。 根据“has-entry”属性的值,它可以在有或没有关联的Gtk.Entry小部件的情况下使用。

实施例¶ T0>

_images/cellrenderercombo_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class CellRendererComboWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererCombo Example")

        self.set_default_size(200, 200)

        liststore_manufacturers = Gtk.ListStore(str)
        manufacturers = ["Sony", "LG",
            "Panasonic", "Toshiba", "Nokia", "Samsung"]
        for item in manufacturers:
            liststore_manufacturers.append([item])

        self.liststore_hardware = Gtk.ListStore(str, str)
        self.liststore_hardware.append(["Television", "Samsung"])
        self.liststore_hardware.append(["Mobile Phone", "LG"])
        self.liststore_hardware.append(["DVD Player", "Sony"])

        treeview = Gtk.TreeView(model=self.liststore_hardware)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
        treeview.append_column(column_text)

        renderer_combo = Gtk.CellRendererCombo()
        renderer_combo.set_property("editable", True)
        renderer_combo.set_property("model", liststore_manufacturers)
        renderer_combo.set_property("text-column", 0)
        renderer_combo.set_property("has-entry", False)
        renderer_combo.connect("edited", self.on_combo_changed)

        column_combo = Gtk.TreeViewColumn("Combo", renderer_combo, text=1)
        treeview.append_column(column_combo)

        self.add(treeview)

    def on_combo_changed(self, widget, path, text):
        self.liststore_hardware[path][1] = text

win = CellRendererComboWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

CellRendererProgress ¶ T0>

Gtk.CellRendererProgress将数值作为单元格中的进度条呈现。 此外,它可以在进度条上显示文本。

可以通过更改“值”属性来修改进度条的百分比值。 Gtk.ProgressBar相似,您可以通过增加“pulse”属性而不是“value”属性来启用活动模式

实施例¶ T0>

_images/cellrendererprogress_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

class CellRendererProgressWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererProgress Example")

        self.set_default_size(200, 200)

        self.liststore = Gtk.ListStore(str, int, bool)
        self.current_iter = self.liststore.append(["Sabayon", 0, False])
        self.liststore.append(["Zenwalk", 0, False])
        self.liststore.append(["SimplyMepis", 0, False])

        treeview = Gtk.TreeView(model=self.liststore)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
        treeview.append_column(column_text)

        renderer_progress = Gtk.CellRendererProgress()
        column_progress = Gtk.TreeViewColumn("Progress", renderer_progress,
            value=1, inverted=2)
        treeview.append_column(column_progress)

        renderer_toggle = Gtk.CellRendererToggle()
        renderer_toggle.connect("toggled", self.on_inverted_toggled)
        column_toggle = Gtk.TreeViewColumn("Inverted", renderer_toggle,
            active=2)
        treeview.append_column(column_toggle)

        self.add(treeview)

        self.timeout_id = GObject.timeout_add(100, self.on_timeout, None)

    def on_inverted_toggled(self, widget, path):
        self.liststore[path][2] = not self.liststore[path][2]

    def on_timeout(self, user_data):
        new_value = self.liststore[self.current_iter][1] + 1
        if new_value > 100:
            self.current_iter = self.liststore.iter_next(self.current_iter)
            if self.current_iter is None:
                self.reset_model()
            new_value = self.liststore[self.current_iter][1] + 1

        self.liststore[self.current_iter][1] = new_value
        return True

    def reset_model(self):
        for row in self.liststore:
            row[1] = 0
        self.current_iter = self.liststore.get_iter_first()

win = CellRendererProgressWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

CellRendererSpin ¶ T0>

Gtk.CellRendererSpin renders text in a cell like Gtk.CellRendererText from which it is derived. 但后者提供了一个简单的条目来编辑文本,Gtk.CellRendererSpin提供了一个Gtk.SpinButton小部件。 当然,这意味着文本必须作为浮点数解析。

旋钮的范围取自单元格渲染器的调整属性,该属性可以显式设置或映射到树模型中的列,如单元格渲染器的所有属性。 Gtk.CellRendererSpin也具有爬升率和要显示的位数的属性。

实施例¶ T0>

_images/cellrendererspin_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class CellRendererSpinWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererSpin Example")

        self.set_default_size(200, 200)

        self.liststore = Gtk.ListStore(str, int)
        self.liststore.append(["Oranges", 5])
        self.liststore.append(["Apples", 4])
        self.liststore.append(["Bananas", 2])

        treeview = Gtk.TreeView(model=self.liststore)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Fruit", renderer_text, text=0)
        treeview.append_column(column_text)

        renderer_spin = Gtk.CellRendererSpin()
        renderer_spin.connect("edited", self.on_amount_edited)
        renderer_spin.set_property("editable", True)

        adjustment = Gtk.Adjustment(0, 0, 100, 1, 10, 0)
        renderer_spin.set_property("adjustment", adjustment)

        column_spin = Gtk.TreeViewColumn("Amount", renderer_spin, text=1)
        treeview.append_column(column_spin)

        self.add(treeview)

    def on_amount_edited(self, widget, path, value):
        self.liststore[path][1] = int(value)

win = CellRendererSpinWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

组合框¶ T0>

一个Gtk.ComboBox允许从下拉菜单中选择一个项目。 他们更喜欢屏幕上有许多单选按钮,因为他们占用较少的空间。 如果合适,它可以显示关于每个项目的额外信息,例如文本,图片,复选框或进度条。

Gtk.ComboBoxGtk.TreeView非常相似,因为它们都使用模型视图模式;有效选择列表以树模型的形式指定,并且可以通过使用cell renderers将选项的显示调整为模型中的数据。 如果组合框包含大量项目,则最好将它们显示在网格中而不是列表中。 这可以通过调用Gtk.ComboBox.set_wrap_width()完成。

Gtk.ComboBox小部件通常会将用户限制为可用选项,但可以选择使用Gtk.Entry,允许用户在没有可用选项时输入任意文本选择是合适的。 为此,请使用静态方法之一Gtk.ComboBox.new_with_entry()Gtk.ComboBox.new_with_model_and_entry()创建一个Gtk.ComboBox

对于文本选择的简单列表,Gtk.ComboBox的模型视图API可能有点压倒性。 在这种情况下,Gtk.ComboBoxText提供了一个简单的选择。 Gtk.ComboBoxGtk.ComboBoxText都可以包含一个条目。

实施例¶ T0>

_images/combobox_example.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
71
72
73
74
75
76
77
78
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class ComboBoxWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="ComboBox Example")

        self.set_border_width(10)

        name_store = Gtk.ListStore(int, str)
        name_store.append([1, "Billy Bob"])
        name_store.append([11, "Billy Bob Junior"])
        name_store.append([12, "Sue Bob"])
        name_store.append([2, "Joey Jojo"])
        name_store.append([3, "Rob McRoberts"])
        name_store.append([31, "Xavier McRoberts"])

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)

        name_combo = Gtk.ComboBox.new_with_model_and_entry(name_store)
        name_combo.connect("changed", self.on_name_combo_changed)
        name_combo.set_entry_text_column(1)
        vbox.pack_start(name_combo, False, False, 0)

        country_store = Gtk.ListStore(str)
        countries = ["Austria", "Brazil", "Belgium", "France", "Germany",
            "Switzerland", "United Kingdom", "United States of America",
            "Uruguay"]
        for country in countries:
            country_store.append([country])

        country_combo = Gtk.ComboBox.new_with_model(country_store)
        country_combo.connect("changed", self.on_country_combo_changed)
        renderer_text = Gtk.CellRendererText()
        country_combo.pack_start(renderer_text, True)
        country_combo.add_attribute(renderer_text, "text", 0)
        vbox.pack_start(country_combo, False, False, True)

        currencies = ["Euro", "US Dollars", "British Pound", "Japanese Yen",
            "Russian Ruble", "Mexican peso", "Swiss franc"]
        currency_combo = Gtk.ComboBoxText()
        currency_combo.set_entry_text_column(0)
        currency_combo.connect("changed", self.on_currency_combo_changed)
        for currency in currencies:
            currency_combo.append_text(currency)

        vbox.pack_start(currency_combo, False, False, 0)

        self.add(vbox)

    def on_name_combo_changed(self, combo):
        tree_iter = combo.get_active_iter()
        if tree_iter is not None:
            model = combo.get_model()
            row_id, name = model[tree_iter][:2]
            print("Selected: ID=%d, name=%s" % (row_id, name))
        else:
            entry = combo.get_child()
            print("Entered: %s" % entry.get_text())

    def on_country_combo_changed(self, combo):
        tree_iter = combo.get_active_iter()
        if tree_iter is not None:
            model = combo.get_model()
            country = model[tree_iter][0]
            print("Selected: country=%s" % country)

    def on_currency_combo_changed(self, combo):
        text = combo.get_active_text()
        if text is not None:
            print("Selected: currency=%s" % text)

win = ComboBoxWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

图标型控件¶ T0>

Gtk.IconView是一个在网格视图中显示图标集合的小部件。 它支持诸如拖放,多选和项目重新排序等功能。

Gtk.TreeView类似,Gtk.IconView为其模型使用Gtk.ListStore Instead of using cell renderers, Gtk.IconView requires that one of the columns in its Gtk.ListStore contains GdkPixbuf.Pixbuf objects.

Gtk.IconView支持多种选择模式,允许一次选择多个图标,将选择限制为只有一个项目,或完全禁止选择项目。 要指定选择模式,Gtk.IconView.set_selection_mode()方法与Gtk.SelectionMode选择模式之一一起使用。

实施例¶ T0>

_images/iconview_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository.GdkPixbuf import Pixbuf

icons = ["edit-cut", "edit-paste", "edit-copy"]

class IconViewWindow(Gtk.Window):

  def __init__(self):
    Gtk.Window.__init__(self)
    self.set_default_size(200, 200)

    liststore = Gtk.ListStore(Pixbuf, str)
    iconview = Gtk.IconView.new()
    iconview.set_model(liststore)
    iconview.set_pixbuf_column(0)
    iconview.set_text_column(1)

    for icon in icons:
        pixbuf = Gtk.IconTheme.get_default().load_icon(icon, 64, 0)
        liststore.append([pixbuf, "Label"])

    self.add(iconview)

win = IconViewWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

多行文本编辑器

Gtk.TextView小部件可用于显示和编辑大量格式化的文本。 Gtk.TreeView一样,它具有模型/视图设计。 在这种情况下,Gtk.TextBuffer是表示正在编辑的文本的模型。 这允许两个或多个Gtk.TextView小部件共享相同的Gtk.TextBuffer,并允许这些文本缓冲区显示稍微不同。 或者,您可以维护多个文本缓冲区,并选择在不同的时间在同一个Gtk.TextView小部件中显示每个文本缓冲区。

视图

Gtk.TextView是用户可以添加,编辑和删除文本数据的前端。 它们通常用于编辑多行文本。 创建Gtk.TextView时,它包含它自己的默认Gtk.TextBuffer,您可以通过Gtk.TextView.get_buffer()方法。

默认情况下,可以从Gtk.TextView添加,编辑和删除文本。 你可以通过调用Gtk.TextView.set_editable()来禁用它。 如果文本不可编辑,那么通常也希望使用Gtk.TextView.set_cursor_visible()隐藏文本光标。 在某些情况下,使用Gtk.TextView.set_justification()设置文本的对齐可能很有用。 文本可以显示在左边缘(Gtk.Justification.LEFT),右边缘(Gtk.Justification.RIGHT),居中(Gtk.Justification.CENTER),或分布在整个宽度(Gtk.Justification.FILL)。

Gtk.TextView小部件的另一个默认设置是长行文本将水平持续,直到输入中断为止。 要包装文本并防止它从屏幕边缘调用Gtk.TextView.set_wrap_mode()

模型

Gtk.TextBufferGtk.TextView小部件的核心,用于保存在Gtk.TextView中显示的任何文本。 。 使用Gtk.TextBuffer.set_text()Gtk.TextBuffer.get_text()可以设置和检索内容。 但是,大多数文本操作都是由迭代器完成的,由Gtk.TextIter表示。 迭代器表示文本缓冲区中两个字符之间的位置。 迭代器无限期地无效;每当缓冲区以影响缓冲区内容的方式进行修改时,所有未完成的迭代程序都将失效。

因此,迭代器不能用于保存缓冲区修改中的位置。 要保留位置,请使用Gtk.TextMark 文本缓冲区包含两个内置标记;一个“插入”标记(它是光标的位置)和“selection_bound”标记。 它们都可以分别使用Gtk.TextBuffer.get_insert()Gtk.TextBuffer.get_selection_bound()进行检索。 默认情况下,不显示Gtk.TextMark的位置。 这可以通过调用Gtk.TextMark.set_visible()来更改。

存在许多方法来检索Gtk.TextIter 例如,Gtk.TextBuffer.get_start_iter()返回指向文本缓冲区中第一个位置的迭代器,而Gtk.TextBuffer.get_end_iter()返回指向过去的迭代器最后一个有效的字符。 通过调用Gtk.TextBuffer.get_selection_bounds()可以获取选定文本的边界。

要在特定位置插入文本,请使用Gtk.TextBuffer.insert() 另一个有用的方法是Gtk.TextBuffer.insert_at_cursor(),它可以在光标当前所在的位置插入文本。 要删除部分文本缓冲区,请使用Gtk.TextBuffer.delete()

In addition, Gtk.TextIter can be used to locate textual matches in the buffer using Gtk.TextIter.forward_search() and Gtk.TextIter.backward_search(). 起点和终点用作搜索的起点,并根据需要向前/向后移动。

标记¶ T0>

缓冲区中的文本可以用标记进行标记。 标签是可应用于某些文本范围的属性。 例如,标签可能被称为“粗体”,并使标签内的文本变为粗体。 然而,标签概念比这更普遍;标签不必影响外观。 它们可以影响鼠标和按键的行为,“锁定”一系列文本,以便用户无法对其进行编辑,或无数其他事情。 标签由Gtk.TextTag对象表示。 一个Gtk.TextTag可以应用于任何数量的缓冲区中的任意数量的文本范围。

每个标签都存储在Gtk.TextTagTable中。 标签表定义了一组可以一起使用的标签。 每个缓冲区都有一个与之关联的标签表;只有来自该标签表的标签才能与缓冲区一起使用。 但是,一个标签表可以在多个缓冲区之间共享。

要指定缓冲区中的某些文本应该具有特定的格式,您必须定义一个标记来保存该格式化信息,然后使用Gtk.TextBuffer.create_tag()将该标记应用于文本区域并Gtk.TextBuffer.apply_tag()

tag = textbuffer.create_tag("orange_bg", background="orange")
textbuffer.apply_tag(tag, start_iter, end_iter)

以下是适用于文本的一些常用样式:

  • 背景颜色(“前景”属性)
  • 前景色(“背景”属性)
  • 下划线(“下划线”属性)
  • 粗体(“重量”属性)
  • 斜体(“样式”属性)
  • 删除线(“删除线”属性)
  • 理由(“理由”财产)
  • 大小(“大小”和“大小点”属性)
  • 文本换行(“wrap-mode”属性)

您也可以稍后使用Gtk.TextBuffer.remove_tag()删除特定标记,或通过调用Gtk.TextBuffer.remove_all_tags()删除给定区域中的所有标记。

实施例¶ T0>

_images/textview_example.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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Pango

class SearchDialog(Gtk.Dialog):

    def __init__(self, parent):
        Gtk.Dialog.__init__(self, "Search", parent,
            Gtk.DialogFlags.MODAL, buttons=(
            Gtk.STOCK_FIND, Gtk.ResponseType.OK,
            Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))

        box = self.get_content_area()

        label = Gtk.Label("Insert text you want to search for:")
        box.add(label)

        self.entry = Gtk.Entry()
        box.add(self.entry)

        self.show_all()

class TextViewWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="TextView Example")

        self.set_default_size(-1, 350)

        self.grid = Gtk.Grid()
        self.add(self.grid)

        self.create_textview()
        self.create_toolbar()
        self.create_buttons()

    def create_toolbar(self):
        toolbar = Gtk.Toolbar()
        self.grid.attach(toolbar, 0, 0, 3, 1)

        button_bold = Gtk.ToolButton()
        button_bold.set_icon_name("format-text-bold-symbolic")
        toolbar.insert(button_bold, 0)

        button_italic = Gtk.ToolButton()
        button_italic.set_icon_name("format-text-italic-symbolic")
        toolbar.insert(button_italic, 1)

        button_underline = Gtk.ToolButton()
        button_underline.set_icon_name("format-text-underline-symbolic")
        toolbar.insert(button_underline, 2)

        button_bold.connect("clicked", self.on_button_clicked, self.tag_bold)
        button_italic.connect("clicked", self.on_button_clicked,
            self.tag_italic)
        button_underline.connect("clicked", self.on_button_clicked,
            self.tag_underline)

        toolbar.insert(Gtk.SeparatorToolItem(), 3)

        radio_justifyleft = Gtk.RadioToolButton()
        radio_justifyleft.set_icon_name("format-justify-left-symbolic")
        toolbar.insert(radio_justifyleft, 4)

        radio_justifycenter = Gtk.RadioToolButton.new_from_widget(radio_justifyleft)
        radio_justifycenter.set_icon_name("format-justify-center-symbolic")
        toolbar.insert(radio_justifycenter, 5)

        radio_justifyright = Gtk.RadioToolButton.new_from_widget(radio_justifyleft)
        radio_justifyright.set_icon_name("format-justify-right-symbolic")
        toolbar.insert(radio_justifyright, 6)

        radio_justifyfill = Gtk.RadioToolButton.new_from_widget(radio_justifyleft)
        radio_justifyfill.set_icon_name("format-justify-fill-symbolic")
        toolbar.insert(radio_justifyfill, 7)

        radio_justifyleft.connect("toggled", self.on_justify_toggled,
            Gtk.Justification.LEFT)
        radio_justifycenter.connect("toggled", self.on_justify_toggled,
            Gtk.Justification.CENTER)
        radio_justifyright.connect("toggled", self.on_justify_toggled,
            Gtk.Justification.RIGHT)
        radio_justifyfill.connect("toggled", self.on_justify_toggled,
            Gtk.Justification.FILL)

        toolbar.insert(Gtk.SeparatorToolItem(), 8)

        button_clear = Gtk.ToolButton()
        button_clear.set_icon_name("edit-clear-symbolic")
        button_clear.connect("clicked", self.on_clear_clicked)
        toolbar.insert(button_clear, 9)

        toolbar.insert(Gtk.SeparatorToolItem(), 10)

        button_search = Gtk.ToolButton()
        button_search.set_icon_name("system-search-symbolic")
        button_search.connect("clicked", self.on_search_clicked)
        toolbar.insert(button_search, 11)

    def create_textview(self):
        scrolledwindow = Gtk.ScrolledWindow()
        scrolledwindow.set_hexpand(True)
        scrolledwindow.set_vexpand(True)
        self.grid.attach(scrolledwindow, 0, 1, 3, 1)

        self.textview = Gtk.TextView()
        self.textbuffer = self.textview.get_buffer()
        self.textbuffer.set_text("This is some text inside of a Gtk.TextView. "
            + "Select text and click one of the buttons 'bold', 'italic', "
            + "or 'underline' to modify the text accordingly.")
        scrolledwindow.add(self.textview)

        self.tag_bold = self.textbuffer.create_tag("bold",
            weight=Pango.Weight.BOLD)
        self.tag_italic = self.textbuffer.create_tag("italic",
            style=Pango.Style.ITALIC)
        self.tag_underline = self.textbuffer.create_tag("underline",
            underline=Pango.Underline.SINGLE)
        self.tag_found = self.textbuffer.create_tag("found",
            background="yellow")

    def create_buttons(self):
        check_editable = Gtk.CheckButton("Editable")
        check_editable.set_active(True)
        check_editable.connect("toggled", self.on_editable_toggled)
        self.grid.attach(check_editable, 0, 2, 1, 1)

        check_cursor = Gtk.CheckButton("Cursor Visible")
        check_cursor.set_active(True)
        check_editable.connect("toggled", self.on_cursor_toggled)
        self.grid.attach_next_to(check_cursor, check_editable,
            Gtk.PositionType.RIGHT, 1, 1)

        radio_wrapnone = Gtk.RadioButton.new_with_label_from_widget(None,
            "No Wrapping")
        self.grid.attach(radio_wrapnone, 0, 3, 1, 1)

        radio_wrapchar = Gtk.RadioButton.new_with_label_from_widget(
            radio_wrapnone, "Character Wrapping")
        self.grid.attach_next_to(radio_wrapchar, radio_wrapnone,
            Gtk.PositionType.RIGHT, 1, 1)

        radio_wrapword = Gtk.RadioButton.new_with_label_from_widget(
            radio_wrapnone, "Word Wrapping")
        self.grid.attach_next_to(radio_wrapword, radio_wrapchar,
            Gtk.PositionType.RIGHT, 1, 1)

        radio_wrapnone.connect("toggled", self.on_wrap_toggled,
            Gtk.WrapMode.NONE)
        radio_wrapchar.connect("toggled", self.on_wrap_toggled,
            Gtk.WrapMode.CHAR)
        radio_wrapword.connect("toggled", self.on_wrap_toggled,
            Gtk.WrapMode.WORD)

    def on_button_clicked(self, widget, tag):
        bounds = self.textbuffer.get_selection_bounds()
        if len(bounds) != 0:
            start, end = bounds
            self.textbuffer.apply_tag(tag, start, end)

    def on_clear_clicked(self, widget):
        start = self.textbuffer.get_start_iter()
        end = self.textbuffer.get_end_iter()
        self.textbuffer.remove_all_tags(start, end)

    def on_editable_toggled(self, widget):
        self.textview.set_editable(widget.get_active())

    def on_cursor_toggled(self, widget):
        self.textview.set_cursor_visible(widget.get_active())

    def on_wrap_toggled(self, widget, mode):
        self.textview.set_wrap_mode(mode)

    def on_justify_toggled(self, widget, justification):
        self.textview.set_justification(justification)

    def on_search_clicked(self, widget):
        dialog = SearchDialog(self)
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            cursor_mark = self.textbuffer.get_insert()
            start = self.textbuffer.get_iter_at_mark(cursor_mark)
            if start.get_offset() == self.textbuffer.get_char_count():
                start = self.textbuffer.get_start_iter()

            self.search_and_mark(dialog.entry.get_text(), start)

        dialog.destroy()

    def search_and_mark(self, text, start):
        end = self.textbuffer.get_end_iter()
        match = start.forward_search(text, 0, end)

        if match is not None:
            match_start, match_end = match
            self.textbuffer.apply_tag(self.tag_found, match_start, match_end)
            self.search_and_mark(text, match_end)

win = TextViewWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

对话框¶ T0>

对话窗口与标准窗口非常相似,用于提供或检索用户的信息。 例如,它们通常用于提供偏好窗口。 对话框的主要区别是一些预先安装的小部件,它们自动布局对话框。 从那里,我们可以简单地添加标签,按钮,检查按钮等。另一个很大的区别是响应的处理,以控制应用程序在对话被交互后应该如何表现。

有几个派生的对话框类,你可能会觉得有用。 Gtk.MessageDialog is used for most simple notifications. 但在其他时候,您可能需要派生自己的对话框类来提供更复杂的功能。

自定义对话框

要将小部件打包到自定义对话框中,应将它们打包到Gtk.Box中,可通过Gtk.Dialog.get_content_area()获取。 为了将Gtk.Button添加到对话框的底部,可以使用Gtk.Dialog.add_button()方法。

A ‘modal’ dialog (that is, one which freezes the rest of the application from user input), can be created by calling Gtk.Dialog.set_modal on the dialog or set the flags argument of the Gtk.Dialog constructor to include the Gtk.DialogFlags.MODAL flag.

点击一个按钮会发出一个叫做“响应”的信号。 如果您想在将控制流返回到代码之前阻止等待对话框返回,可以调用Gtk.Dialog.run() 该方法返回一个int值,它可以是Gtk.ResponseType中的一个值,也可以是您在Gtk.Dialog构造函数或Gtk.Dialog.add_button()

最后,有两种方法可以删除对话框。 Gtk.Widget.hide()方法从视图中删除对话框,但将其保存在内存中。 如果需要稍后访问,则有助于防止再次构造对话框。 或者,一旦不再需要,可以使用Gtk.Widget.destroy()方法从内存中删除该对话框。 需要注意的是,如果对话框在被销毁后需要被访问,则需要重新构造该对话框,否则对话框窗口将是空的。

实施例¶ T0>

_images/dialog_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class DialogExample(Gtk.Dialog):

    def __init__(self, parent):
        Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OK, Gtk.ResponseType.OK))

        self.set_default_size(150, 100)

        label = Gtk.Label("This is a dialog to display additional information")

        box = self.get_content_area()
        box.add(label)
        self.show_all()

class DialogWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Dialog Example")

        self.set_border_width(6)

        button = Gtk.Button("Open dialog")
        button.connect("clicked", self.on_button_clicked)

        self.add(button)

    def on_button_clicked(self, widget):
        dialog = DialogExample(self)
        response = dialog.run()

        if response == Gtk.ResponseType.OK:
            print("The OK button was clicked")
        elif response == Gtk.ResponseType.CANCEL:
            print("The Cancel button was clicked")

        dialog.destroy()

win = DialogWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

MessageDialog ¶ T0>

Gtk.MessageDialog是一个便利的类,用于创建简单的标准消息对话框,并带有用于用户响应的消息,图标和按钮您可以在Gtk.MessageDialog构造函数,并指定标准按钮。

在某些需要进一步解释发生事件的对话框中,可以添加辅助文本。 在这种情况下,创建消息对话框时输入的主要消息变大并设置为粗体文本。 辅助消息可以通过调用Gtk.MessageDialog.format_secondary_text()来设置。

实施例¶ T0>

_images/messagedialog_example.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
71
72
73
74
75
76
77
78
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class MessageDialogWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="MessageDialog Example")

        box = Gtk.Box(spacing=6)
        self.add(box)

        button1 = Gtk.Button("Information")
        button1.connect("clicked", self.on_info_clicked)
        box.add(button1)

        button2 = Gtk.Button("Error")
        button2.connect("clicked", self.on_error_clicked)
        box.add(button2)

        button3 = Gtk.Button("Warning")
        button3.connect("clicked", self.on_warn_clicked)
        box.add(button3)

        button4 = Gtk.Button("Question")
        button4.connect("clicked", self.on_question_clicked)
        box.add(button4)

    def on_info_clicked(self, widget):
        dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK, "This is an INFO MessageDialog")
        dialog.format_secondary_text(
            "And this is the secondary text that explains things.")
        dialog.run()
        print("INFO dialog closed")

        dialog.destroy()

    def on_error_clicked(self, widget):
        dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR,
            Gtk.ButtonsType.CANCEL, "This is an ERROR MessageDialog")
        dialog.format_secondary_text(
            "And this is the secondary text that explains things.")
        dialog.run()
        print("ERROR dialog closed")

        dialog.destroy()

    def on_warn_clicked(self, widget):
        dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.WARNING,
            Gtk.ButtonsType.OK_CANCEL, "This is an WARNING MessageDialog")
        dialog.format_secondary_text(
            "And this is the secondary text that explains things.")
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            print("WARN dialog closed by clicking OK button")
        elif response == Gtk.ResponseType.CANCEL:
            print("WARN dialog closed by clicking CANCEL button")

        dialog.destroy()

    def on_question_clicked(self, widget):
        dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.QUESTION,
            Gtk.ButtonsType.YES_NO, "This is an QUESTION MessageDialog")
        dialog.format_secondary_text(
            "And this is the secondary text that explains things.")
        response = dialog.run()
        if response == Gtk.ResponseType.YES:
            print("QUESTION dialog closed by clicking YES button")
        elif response == Gtk.ResponseType.NO:
            print("QUESTION dialog closed by clicking NO button")

        dialog.destroy()

win = MessageDialogWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

FileChooserDialog ¶ T0>

Gtk.FileChooserDialog适用于“文件/打开”或“文件/保存”菜单项。 您可以在文件选择器对话框中使用所有的Gtk.FileChooser方法,也可以使用Gtk.Dialog中的所有方法。

创建Gtk.FileChooserDialog时,您必须定义对话框的用途:

Gtk.FileChooserDialog继承自Gtk.Dialog,所以按钮具有响应ID,例如Gtk.ResponseType.ACCEPTGtk.ResponseType.CANCEL,它可以在Gtk.FileChooserDialog构造函数中指定。 Gtk.Dialog相比,您不能在Gtk.FileChooserDialog中使用自定义响应代码。 它预计至少有一个按钮将具有以下响应ID:

当用户完成文件选择时,程序可以将选定的名称作为文件名(Gtk.FileChooser.get_filename())或URIs(Gtk.FileChooser.get_uri()

默认情况下,Gtk.FileChooser只允许一次选择一个文件。 要启用多个文件,请使用Gtk.FileChooser.set_select_multiple() 使用Gtk.FileChooser.get_filenames()Gtk.FileChooser.get_uris()检索选定文件的列表。

Gtk.FileChooser还支持多种选项,使文件和文件夹更易于配置和访问。

此外,您可以通过创建Gtk.FileFilter对象并调用Gtk.FileChooser.add_filter()来指定显示哪种文件。 用户然后可以从文件选择器底部的组合框中选择一个添加的过滤器。

实施例¶ T0>

_images/filechooserdialog_example.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
71
72
73
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class FileChooserWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="FileChooser Example")

        box = Gtk.Box(spacing=6)
        self.add(box)

        button1 = Gtk.Button("Choose File")
        button1.connect("clicked", self.on_file_clicked)
        box.add(button1)

        button2 = Gtk.Button("Choose Folder")
        button2.connect("clicked", self.on_folder_clicked)
        box.add(button2)

    def on_file_clicked(self, widget):
        dialog = Gtk.FileChooserDialog("Please choose a file", self,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OPEN, Gtk.ResponseType.OK))

        self.add_filters(dialog)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            print("Open clicked")
            print("File selected: " + dialog.get_filename())
        elif response == Gtk.ResponseType.CANCEL:
            print("Cancel clicked")

        dialog.destroy()

    def add_filters(self, dialog):
        filter_text = Gtk.FileFilter()
        filter_text.set_name("Text files")
        filter_text.add_mime_type("text/plain")
        dialog.add_filter(filter_text)

        filter_py = Gtk.FileFilter()
        filter_py.set_name("Python files")
        filter_py.add_mime_type("text/x-python")
        dialog.add_filter(filter_py)

        filter_any = Gtk.FileFilter()
        filter_any.set_name("Any files")
        filter_any.add_pattern("*")
        dialog.add_filter(filter_any)

    def on_folder_clicked(self, widget):
        dialog = Gtk.FileChooserDialog("Please choose a folder", self,
            Gtk.FileChooserAction.SELECT_FOLDER,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             "Select", Gtk.ResponseType.OK))
        dialog.set_default_size(800, 400)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            print("Select clicked")
            print("Folder selected: " + dialog.get_filename())
        elif response == Gtk.ResponseType.CANCEL:
            print("Cancel clicked")

        dialog.destroy()

win = FileChooserWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Popovers ¶ T0>

Gtk.Popover是一个用于显示附加信息的独立窗口,通常用作按钮菜单和上下文菜单的一部分。 它们的用途类似于对话窗口的用途,具有较少的破坏性,并且与弹出窗口指向的小部件有连接。 Popovers通过视觉连接到一个小三角形的相关小部件。

Popover可以用Gtk.Popover创建;用于打开弹出窗口使用Gtk.Widget.show_all()

自定义弹出窗口

可以使用Gtk.Container.add()将小部件添加到弹出窗口。

实施例¶ T0>

_images/popover_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class PopoverWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Popover Demo")
        self.set_border_width(10)

        outerbox = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
        self.add(outerbox)

        button = Gtk.Button.new_with_label("Click Me")
        button.connect("clicked", self.on_click_me_clicked)
        outerbox.pack_start(button, False, True, 0)

        self.popover = Gtk.Popover()
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(Gtk.ModelButton("Item 1"), False, True, 10)
        vbox.pack_start(Gtk.Label("Item 2"), False, True, 10)
        self.popover.add(vbox)
        self.popover.set_position(Gtk.PositionType.BOTTOM)

    def on_click_me_clicked(self, button):
        self.popover.set_relative_to(button)
        self.popover.show_all()
        self.popover.popup()

    def on_open_clicked(self, button):
        print("\"Open\" button was clicked")


win = PopoverWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

剪贴板¶ T0>

Gtk.Clipboard为各种数据提供了一个存储区域,包括文本和图像。 使用剪贴板可以通过诸如复制,剪切和粘贴等操作在应用程序之间共享这些数据。 这些操作通常以三种方式完成:使用键盘快捷键,使用Gtk.MenuItem,并将函数连接到Gtk.Button小部件。

有多个剪贴板选择用于不同的目的。 在大多数情况下,名为CLIPBOARD的选择用于日常复制和粘贴。 PRIMARY is another common selection which stores text selected by the user with the cursor.

实施例¶ T0>

_images/clipboard_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

class ClipboardWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Clipboard Example")

        table = Gtk.Table(3, 2)

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
        self.entry = Gtk.Entry()
        self.image = Gtk.Image.new_from_icon_name("process-stop", Gtk.IconSize.MENU)

        button_copy_text = Gtk.Button("Copy Text")
        button_paste_text = Gtk.Button("Paste Text")
        button_copy_image = Gtk.Button("Copy Image")
        button_paste_image = Gtk.Button("Paste Image")

        table.attach(self.entry, 0, 1, 0, 1)
        table.attach(self.image, 0, 1, 1, 2)
        table.attach(button_copy_text, 1, 2, 0, 1)
        table.attach(button_paste_text, 2, 3, 0, 1)
        table.attach(button_copy_image, 1, 2, 1, 2)
        table.attach(button_paste_image, 2, 3, 1, 2)

        button_copy_text.connect("clicked", self.copy_text)
        button_paste_text.connect("clicked", self.paste_text)
        button_copy_image.connect("clicked", self.copy_image)
        button_paste_image.connect("clicked", self.paste_image)

        self.add(table)

    def copy_text(self, widget):
        self.clipboard.set_text(self.entry.get_text(), -1)

    def paste_text(self, widget):
        text = self.clipboard.wait_for_text()
        if text is not None:
            self.entry.set_text(text)
        else:
            print("No text on the clipboard.")

    def copy_image(self, widget):
        if self.image.get_storage_type() == Gtk.ImageType.PIXBUF:
            self.clipboard.set_image(self.image.get_pixbuf())
        else:
            print("No image has been pasted yet.")

    def paste_image(self, widget):
        image = self.clipboard.wait_for_image()
        if image is not None:
            self.image.set_from_pixbuf(image)


win = ClipboardWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

拖放

注意

PyGObject <3.0.3的版本包含一个不允许拖放功能正常工作的错误。 因此,以下示例需要使用PyGObject> = 3.0.3版本。

在小部件之间设置拖放包括使用Gtk.Widget.drag_source_set()方法选择一个拖动源(用户开始拖动的小部件),选择一个拖动目标(小部件用户放弃)与Gtk.Widget.drag_dest_set()方法,然后处理这两个小部件上的相关信号。

而不是使用Gtk.Widget.drag_source_set()Gtk.Widget.drag_dest_set()一些特定的小部件需要使用特定的功能(如Gtk.TreeViewGtk.IconView)。

基本的拖放只需要源连接到“拖动数据获取”信号,而目的地连接到“拖动数据接收”信号。 更复杂的事情,如特定的放置区域和自定义拖动图标将需要连接到additional signals,并与它提供的Gdk.DragContext对象交互。

In order to transfer data between the source and destination, you must interact with the Gtk.SelectionData variable supplied in the “drag-data-get” and “drag-data-received” signals using the Gtk.SelectionData get and set methods.

目标条目

为了允许拖动源和目的地知道他们正在接收和发送什么数据,需要Gtk.TargetEntry's的公共列表。 Gtk.TargetEntry描述将由拖动源发送并由拖动目标接收的一段数据。

有两种方法可以将Gtk.TargetEntry's添加到源和目标。 If the drag and drop is simple and each target entry is of a different type, you can use the group of methods mentioned here.

If you require more than one type of data or wish to do more complex things with the data, you will need to create the Gtk.TargetEntry's using the Gtk.TargetEntry.new() method.

拖动源信号

名称 当它被发射时 共同的目标
拖动开始 用户开始拖动 设置拖动图标
拖动数据GET 当拖动数据被目的地请求时 将数据从源拖移到目标
拖动数据删除 当动作Gdk.DragAction.MOVE的拖动完成时 从源删除数据以完成“移动”
拖动结束 当拖动完成时 撤销在拖动开始时完成的任何事情

拖动目标信号

名称 当它被发射时 共同的目标
拖放运动 拖动图标在拖放区域上移动 只允许将某些区域放在上面
拖放 图标被拖放到拖动区域 只允许将某些区域放在上面
拖动数据收到 当拖动数据被目标接收时 将数据从源拖移到目标

实施例¶ T0>

_images/drag_and_drop_example.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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf

(TARGET_ENTRY_TEXT, TARGET_ENTRY_PIXBUF) = range(2)
(COLUMN_TEXT, COLUMN_PIXBUF) = range(2)

DRAG_ACTION = Gdk.DragAction.COPY

class DragDropWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Drag and Drop Demo")

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)

        hbox = Gtk.Box(spacing=12)
        vbox.pack_start(hbox, True, True, 0)

        self.iconview = DragSourceIconView()
        self.drop_area = DropArea()

        hbox.pack_start(self.iconview, True, True, 0)
        hbox.pack_start(self.drop_area, True, True, 0)

        button_box = Gtk.Box(spacing=6)
        vbox.pack_start(button_box, True, False, 0)

        image_button = Gtk.RadioButton.new_with_label_from_widget(None,
            "Images")
        image_button.connect("toggled", self.add_image_targets)
        button_box.pack_start(image_button, True, False, 0)

        text_button = Gtk.RadioButton.new_with_label_from_widget(image_button,
            "Text")
        text_button.connect("toggled", self.add_text_targets)
        button_box.pack_start(text_button, True, False, 0)

        self.add_image_targets()

    def add_image_targets(self, button=None):
        targets = Gtk.TargetList.new([])
        targets.add_image_targets(TARGET_ENTRY_PIXBUF, True)

        self.drop_area.drag_dest_set_target_list(targets)
        self.iconview.drag_source_set_target_list(targets)

    def add_text_targets(self, button=None):
        self.drop_area.drag_dest_set_target_list(None)
        self.iconview.drag_source_set_target_list(None)

        self.drop_area.drag_dest_add_text_targets()
        self.iconview.drag_source_add_text_targets()

class DragSourceIconView(Gtk.IconView):

    def __init__(self):
        Gtk.IconView.__init__(self)
        self.set_text_column(COLUMN_TEXT)
        self.set_pixbuf_column(COLUMN_PIXBUF)

        model = Gtk.ListStore(str, GdkPixbuf.Pixbuf)
        self.set_model(model)
        self.add_item("Item 1", "image-missing")
        self.add_item("Item 2", "help-about")
        self.add_item("Item 3", "edit-copy")

        self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [],
            DRAG_ACTION)
        self.connect("drag-data-get", self.on_drag_data_get)

    def on_drag_data_get(self, widget, drag_context, data, info, time):
        selected_path = self.get_selected_items()[0]
        selected_iter = self.get_model().get_iter(selected_path)

        if info == TARGET_ENTRY_TEXT:
            text = self.get_model().get_value(selected_iter, COLUMN_TEXT)
            data.set_text(text, -1)
        elif info == TARGET_ENTRY_PIXBUF:
            pixbuf = self.get_model().get_value(selected_iter, COLUMN_PIXBUF)
            data.set_pixbuf(pixbuf)

    def add_item(self, text, icon_name):
        pixbuf = Gtk.IconTheme.get_default().load_icon(icon_name, 16, 0)
        self.get_model().append([text, pixbuf])


class DropArea(Gtk.Label):

    def __init__(self):
        Gtk.Label.__init__(self, "Drop something on me!")
        self.drag_dest_set(Gtk.DestDefaults.ALL, [], DRAG_ACTION)

        self.connect("drag-data-received", self.on_drag_data_received)

    def on_drag_data_received(self, widget, drag_context, x,y, data,info, time):
        if info == TARGET_ENTRY_TEXT:
            text = data.get_text()
            print("Received text: %s" % text)

        elif info == TARGET_ENTRY_PIXBUF:
            pixbuf = data.get_pixbuf()
            width = pixbuf.get_width()
            height = pixbuf.get_height()

            print("Received pixbuf with width %spx and height %spx" % (width,
                height))

win = DragDropWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Glade和Gtk.Builder

Gtk.Builder类为您提供了设计用户界面而无需编写单行代码的机会。 这可以通过用XML文件描述接口,然后在运行时加载XML描述并自动创建对象,Builder类为您完成。 为了不需要手动编写XML,使用Glade应用程序可以以所见即所得(您所看到的是您所得到的)方式创建用户界面

这种方法有几个优点:

  • 需要编写更少的代码。
  • 用户界面的变化可以更快地看到,所以用户界面能够改善。
  • 没有编程技能的设计人员可以创建和编辑UI。
  • 用户界面的描述与正在使用的编程语言无关。

处理由用户触发的界面更改仍需要代码,但Gtk.Builder允许您专注于实现该功能。

创建和加载.glade文件

首先你必须下载并安装Glade。 有关于Glade的几个教程,所以这里不详细解释。 我们首先创建一个窗口,其中包含一个按钮,并将其保存到名为example.glade的文件中。 生成的XML文件应该如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use_action_appearance">False</property>
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="receives_default">True</property>
        <property name="use_action_appearance">False</property>
      </object>
    </child>
  </object>
</interface>

要在Python中加载这个文件,我们需要一个Gtk.Builder对象。

builder = Gtk.Builder()
builder.add_from_file("example.glade")

第二行将example.glade中定义的所有对象加载到Builder对象中。

也可以仅加载一些对象。 下面这行只会添加元组中给出的对象(及其子对象)。

# we don't really have two buttons here, this is just an example
builder.add_objects_from_file("example.glade", ("button1", "button2"))

这两种方法也适用于从字符串而不是文件加载。 它们的对应名称是Gtk.Builder.add_from_string()Gtk.Builder.add_objects_from_string(),它们只是取一个XML字符串而不是文件名。

访问窗口小部件

现在窗口和按钮都被加载了,我们也想显示它们。 因此必须在窗口上调用Gtk.Window.show_all()方法。 但是,我们如何访问关联的对象?

window = builder.get_object("window1")
window.show_all()

每个小部件都可以通过Gtk.Builder.get_object()方法和小部件的id从构建器中检索。 简单的说是真的。

也有可能获得所有对象的列表

builder.get_objects()

连接信号

Glade还可以定义可以连接到代码中的处理程序的信号,而无需从构建器中提取每个对象并手动连接到信号。 首先要做的是在Glade中声明信号名称。 在这个例子中,我们将在窗口关闭时以及按钮被按下时执行操作,因此我们将名称“onDestroy”指定给处理窗口的“销毁”信号的回调,并将“onButtonPressed”赋给处理“按下”按钮的信号。 现在XML文件应该看起来像这样。

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use_action_appearance">False</property>
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="receives_default">True</property>
        <property name="use_action_appearance">False</property>
        <signal name="pressed" handler="onButtonPressed" swapped="no"/>
      </object>
    </child>
  </object>
</interface>

现在我们必须在代码中定义处理函数。 onDestroy应该简单地导致对Gtk.main_quit()的调用。 当按下按钮时,我们想打印字符串“Hello World!”,所以我们按如下定义处理程序

def hello(button):
    print("Hello World!")

接下来,我们必须连接信号和处理函数。 最简单的方法是使用名称到处理程序的映射来定义dict,然后将它传递给Gtk.Builder.connect_signals()方法。

handlers = {
    "onDestroy": Gtk.main_quit,
    "onButtonPressed": hello
}
builder.connect_signals(handlers)

另一种方法是创建一个类,该类具有被称为信号的方法。 在我们的例子中,最后的代码片段可以被重写为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from gi.repository import Gtk

class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")

builder.connect_signals(Handler())

实施例¶ T0>

示例的最终代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")

builder = Gtk.Builder()
builder.add_from_file("builder_example.glade")
builder.connect_signals(Handler())

window = builder.get_object("window1")
window.show_all()

Gtk.main()

对象¶ T0>

GObject是提供基于GObject的GTK +,Pango和其他库中所有对象类型的通用属性和方法的基本类型。 The GObject.GObject class provides methods for object construction and destruction, property access methods, and signal support.

本节将介绍有关Python中GObject实现的一些重要方面。

从GObject.GObject继承

本地GObject可通过GObject.GObject访问。 它很少直接实例化,我们通常使用继承类。 Gtk.WidgetGObject.GObject的继承类。 让一个继承的类创建一个新的小部件,比如设置对话框可能会很有趣。

要继承GObject.GObject,必须在构造函数中调用GObject.GObject.__init__()(如果类继承自Gtk.Button ,它必须调用Gtk.Button.__init__()),如下例所示:

from gi.repository import GObject

class MyObject(GObject.GObject):

    def __init__(self):
        GObject.GObject.__init__(self)

信号¶ T0>

信号将任意应用程序特定的事件与任意数量的监听器连接起来。 例如,在GTK +中,从X服务器接收每个用户事件(按键或鼠标移动),并在给定对象实例上以信号发射的形式生成一个GTK +事件。

每个信号都与类型系统一起注册在类型系统中,当它们注册要在信号发射时调用的函数时,该类型的用户被称为连接到给定类型实例上的信号。 用户也可以自己发出信号,或者停止从与信号相连的其中一个功能中发出信号。

接收信号

参见Main loop and Signals

创建新的信号

New signals can be created by adding them to GObject.GObject.__gsignals__, a dictionary:

当一个新的信号被创建时,方法处理器也可以被定义,每当信号被发射时它都会被调用。 它被称为do_signal_name。

class MyObject(GObject.GObject):
    __gsignals__ = {
        'my_signal': (GObject.SIGNAL_RUN_FIRST, None,
                      (int,))
    }

    def do_my_signal(self, arg):
        print("class method for `my_signal' called with argument", arg)

GObject.SIGNAL_RUN_FIRST indicates that this signal will invoke the object method handler (do_my_signal() here) in the first emission stage. Alternatives are GObject.SIGNAL_RUN_LAST (the method handler will be invoked in the third emission stage) and GObject.SIGNAL_RUN_CLEANUP (invoke the method handler in the last emission stage).

第二部分None表示信号的返回类型,通常为None

(int,)表示信号参数,这里信号只接受一个参数,其类型为int。 此参数类型列表必须以逗号结尾。

信号可以使用GObject.GObject.emit()发射:

my_obj.emit("my_signal", 42) # emit the signal "my_signal", with the
                             # argument 42

属性¶ T0>

GObject的一个很好的特性是它的对象属性的通用get / set机制。 GObject.GObject继承的每个类都可以定义新的属性。 每个属性都有一个永远不会改变的类型(例如str,float,int ...)。 例如,它们用于Gtk.Button,其中有一个包含按钮文本的“label”属性。

使用现有的属性

The class GObject.GObject provides several useful functions to manage existing properties, GObject.GObject.get_property() and GObject.GObject.set_property().

一些属性也具有专用于它们的功能,称为getter和setter。 对于按钮的属性“标签”,有两个函数来获取和设置它们:Gtk.Button.get_label()Gtk.Button.set_label()

创建新的属性

一个属性用一个名称和一个类型来定义。 即使Python本身是动态类型的,一旦定义属性,你就不能改变它的类型。 可以使用GObject.Property创建一个属性。

from gi.repository import GObject

class MyObject(GObject.GObject):

    foo = GObject.Property(type=str, default='bar')
    property_float = GObject.Property(type=float)
    def __init__(self):
        GObject.GObject.__init__(self)

如果您希望某些属性可读但不可写,则属性也可以是只读的。 为此,您可以将一些标志添加到属性定义中,以控制读/写访问。 标志是GObject.ParamFlags.READABLE(仅用于外部代码的读取访问),GObject.ParamFlags.WRITABLE(仅写访问),GObject.ParamFlags.READWRITE

foo = GObject.Property(type=str, flags = GObject.ParamFlags.READABLE) # not writable
bar = GObject.Property(type=str, flags = GObject.ParamFlags.WRITABLE) # not readable

您还可以使用用GObject.Property修饰的新方法定义新的只读属性:

from gi.repository import GObject

class MyObject(GObject.GObject):

    def __init__(self):
        GObject.GObject.__init__(self)

    @GObject.Property
    def readonly(self):
        return 'This is read-only.'

您可以使用以下方式获取此属性

my_object = MyObject()
print(my_object.readonly)
print(my_object.get_property("readonly"))

GObject.Property的API类似于内置的property() 您可以用类似于Python属性的方式创建属性设置器:

class AnotherObject(GObject.Object):
    value = 0

    @GObject.Property
    def prop(self):
        'Read only property.'
        return 1

    @GObject.Property(type=int)
    def propInt(self):
        'Read-write integer property.'
        return self.value

    @propInt.setter
    def propInt(self, value):
        self.value = value

还有一种方法可以使用更详细的形式来定义数字的最小值和最大值:

from gi.repository import GObject

class MyObject(GObject.GObject):

    __gproperties__ = {
        "int-prop": (int, # type
                     "integer prop", # nick
                     "A property that contains an integer", # blurb
                     1, # min
                     5, # max
                     2, # default
                     GObject.ParamFlags.READWRITE # flags
                    ),
    }

    def __init__(self):
        GObject.GObject.__init__(self)
        self.int_prop = 2

    def do_get_property(self, prop):
        if prop.name == 'int-prop':
            return self.int_prop
        else:
            raise AttributeError('unknown property %s' % prop.name)

    def do_set_property(self, prop, value):
        if prop.name == 'int-prop':
            self.int_prop = value
        else:
            raise AttributeError('unknown property %s' % prop.name)

属性必须在GObject.GObject.__gproperties__,一个字典中定义,并在do_get_property和do_set_property中处理。

观看属性

当一个属性被修改时,会发出一个名为“notify :: property-name”的信号:

my_object = MyObject()

def on_notify_foo(obj, gparamstring):
    print("foo changed")

my_object.connect("notify::foo", on_notify_foo)

my_object.set_property("foo", "bar") # on_notify_foo will be called

请注意,当连接到通知信号时,必须使用规范属性名称,如GObject.Object.signals.notify()中所述。 例如,对于Python属性foo_bar_baz,您可以使用以下命令连接到信号notify :: foo-bar-baz

my_object = MyObject()

def on_notify_foo_bar_baz(obj, gparamstring):
    print("foo_bar_baz changed")

my_object.connect("notify::foo-bar-baz", on_notify_foo_bar_baz)

API ¶ T0>

GObject的。 GObject的 T0> ¶ T1>
get_property T0> ( T1> PROPERTY_NAME T2> ) T3> ¶ T4>

检索属性值。

set_property(property_name, value)

将属性property_name设置为

EMIT T0> ( T1> signal_name T2>, ...) T0> ¶ T1>

发出信号signal_name 必须遵循信号参数,例如如果你的信号是(int,)类型的,它必须用:

self.emit(signal_name, 42)
freeze_notify T0> ( T1> ) T2> ¶ T3>

此方法冻结所有“notify ::”信号(在更改任何属性时发出)直到调用thaw_notify()方法。

它建议在调用freeze_notify()时使用语句,这样可以确保thaw_notify()该块:

with an_object.freeze_notify():
    # Do your work here
    ...
thaw_notify T0> ( T1> ) T2> ¶ T3>

解冻所有由freeze_notify()解冻的“notify ::”信号。

建议不要明确地调用thaw_notify(),而要将freeze_notify()一起使用语句。

handler_block T0> ( T1> handler_id的 T2> ) T3> ¶ T4>

阻止实例的处理程序,以便在任何信号发射期间不会调用它,除非为handler_id调用handler_unblock() 因此,“阻塞”信号处理程序意味着暂时停用它,信号处理程序必须被解除阻塞的时间与之前阻塞的时间相同,才能再次激活。

建议将handler_block()结合语句一起使用,该语句将隐含地在块末尾调用handler_unblock()

with an_object.handler_block(handler_id):
    # Do your work here
    ...
handler_unblock T0> ( T1> handler_id的 T2> ) T3> ¶ T4>

取消handler_block()的效果。 被阻塞的处理程序在信号发射期间被跳过,并且直到它被解除阻塞之前,它才被调用正确的时间量。

建议不要显式地调用handler_unblock(),而是将handler_block()一起用语句。

__ gsignals __ T0> ¶ T1>

一个字典,其中继承类可以定义新的信号。

字典中的每个元素都是一个新的信号。 关键是信号名称。 该值是一个元组,格式为:

(GObject.SIGNAL_RUN_FIRST, None, (int,))

GObject.SIGNAL_RUN_FIRST can be replaced with GObject.SIGNAL_RUN_LAST or GObject.SIGNAL_RUN_CLEANUP. None是信号的返回类型。 (int,)是信号参数的列表,它必须以逗号结尾。

__ gproperties __ T0> ¶ T1>

__gproperties__字典是一个类属性,您可以在其中定义对象的属性。 这不是推荐的定义新属性的方法,上面写的方法不那么冗长。 此方法的好处是可以使用更多设置来定义属性,例如数字的最小值或最大值。

关键是财产的名称

该值是描述属性的元组。 这个元组的元素个数取决于它的第一个元素,但是这个元组至少会包含以下几项:

第一个元素是属性的类型(例如intfloat ...)。

第二个元素是属性的昵称,它是一个带有该属性简短描述的字符串。 这通常被具有强大内省功能的程序使用,如图形用户界面构建器Glade

第三个是物业的描述或blurb,这是另一个字符串,对物业有较长的描述。 也被Glade和类似程序使用。

The last one (which is not necessarily the forth one as we will see later) is the property’s flags: GObject.PARAM_READABLE, GObject.PARAM_WRITABLE, GObject.PARAM_READWRITE.

元组的绝对长度取决于属性类型(元组的第一个元素)。 因此我们有以下情况:

如果类型是boolstr,则第四个元素是该属性的默认值。

如果类型为intfloat,则第四个元素为最小接受值,第五个元素为最大接受值,第六个元素为默认值。

如果该类型不是其中之一,则不需要额外的元素。

GObject的。 SIGNAL_RUN_FIRST T0> ¶ T1>

在第一个排放阶段调用对象方法处理程序。

GObject的。 SIGNAL_RUN_LAST T0> ¶ T1>

在第三个排放阶段调用对象方法处理程序。

GObject的。 SIGNAL_RUN_CLEANUP T0> ¶ T1>

在上一个发射阶段调用对象方法处理程序。

GObject.ParamFlags。 READABLE T0> ¶ T1>

该属性是可读的。

GObject.ParamFlags。 WRITABLE T0> ¶ T1>

该属性是可写的。

GObject.ParamFlags。 READWRITE T0> ¶ T1>

该属性是可读写的。

应用¶ T0>

Gtk.Application涵盖了现代应用程序需要的许多重复任务,例如处理多个实例,D-Bus激活,打开文件,命令行解析,启动/关闭,菜单管理,窗口管理等。

操作¶ T0>

Gio.Action是一种公开应用程序或小部件通过名称执行的任何单个任务的方法。 这些操作可以在运行时禁用/启用,并且可以激活它们或更改状态(如果它们包含状态)。

使用操作的原因是从UI中分离出逻辑。 例如,这允许在OSX上使用菜单栏和在GNOME上使用齿轮菜单,只需简单引用操作的名称即可。 你将要使用的主要实现是Gio.SimpleAction,稍后会显示。

许多类如Gio.MenuItemGtk.ModelButton都支持设置动作名称的属性。

这些操作可以组成一个Gio.ActionGroup,当这些组通过Gtk.Widget.insert_action_group()添加到窗口小部件时,它们将获得一个前缀。 比如添加到Gtk.ApplicationWindow时的“win”。 您将在引用它时使用完整的操作名称,例如“app.about”,但是当您创建操作时,它只会“添加”到应用程序中。

您还可以通过在Gio.Menu文件中设置accel属性或使用Gtk.Application.add_accelerator()

命令行

创建应用程序时,它需要Gio.ApplicationFlags的标志属性。 使用这个你可以让它处理所有事情或者有更多的自定义行为。

您可以使用HANDLES_COMMAND_LINEGio.Application.do_command_line()中允许自定义行为。 结合Gio.Application.add_main_option()添加自定义选项。

使用HANDLES_OPEN将完成简单的文件参数处理工作,并让您在Gio.Application.do_open()中处理它。

如果你的应用程序已经打开,这些都将被发送到现有的实例,除非你使用NON_UNIQUE来允许多个实例。

实施例¶ T0>

_images/application_example.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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import sys

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gio, Gtk

# This would typically be its own file
MENU_XML="""
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <menu id="app-menu">
    <section>
      <attribute name="label" translatable="yes">Change label</attribute>
      <item>
        <attribute name="action">win.change_label</attribute>
        <attribute name="target">String 1</attribute>
        <attribute name="label" translatable="yes">String 1</attribute>
      </item>
      <item>
        <attribute name="action">win.change_label</attribute>
        <attribute name="target">String 2</attribute>
        <attribute name="label" translatable="yes">String 2</attribute>
      </item>
      <item>
        <attribute name="action">win.change_label</attribute>
        <attribute name="target">String 3</attribute>
        <attribute name="label" translatable="yes">String 3</attribute>
      </item>
    </section>
    <section>
      <item>
        <attribute name="action">win.maximize</attribute>
        <attribute name="label" translatable="yes">Maximize</attribute>
      </item>
    </section>
    <section>
      <item>
        <attribute name="action">app.about</attribute>
        <attribute name="label" translatable="yes">_About</attribute>
      </item>
      <item>
        <attribute name="action">app.quit</attribute>
        <attribute name="label" translatable="yes">_Quit</attribute>
        <attribute name="accel">&lt;Primary&gt;q</attribute>
    </item>
    </section>
  </menu>
</interface>
"""

class AppWindow(Gtk.ApplicationWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # This will be in the windows group and have the "win" prefix
        max_action = Gio.SimpleAction.new_stateful("maximize", None,
                                           GLib.Variant.new_boolean(False))
        max_action.connect("change-state", self.on_maximize_toggle)
        self.add_action(max_action)

        # Keep it in sync with the actual state
        self.connect("notify::is-maximized",
                            lambda obj, pspec: max_action.set_state(
                                               GLib.Variant.new_boolean(obj.props.is_maximized)))

        lbl_variant = GLib.Variant.new_string("String 1")
        lbl_action = Gio.SimpleAction.new_stateful("change_label", lbl_variant.get_type(),
                                               lbl_variant)
        lbl_action.connect("change-state", self.on_change_label_state)
        self.add_action(lbl_action)

        self.label = Gtk.Label(label=lbl_variant.get_string(),
                               margin=30)
        self.add(self.label)
        self.label.show()

    def on_change_label_state(self, action, value):
        action.set_state(value)
        self.label.set_text(value.get_string())

    def on_maximize_toggle(self, action, value):
        action.set_state(value)
        if value.get_boolean():
            self.maximize()
        else:
            self.unmaximize()

class Application(Gtk.Application):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
                         **kwargs)
        self.window = None

        self.add_main_option("test", ord("t"), GLib.OptionFlags.NONE,
                             GLib.OptionArg.NONE, "Command line test", None)

    def do_startup(self):
        Gtk.Application.do_startup(self)

        action = Gio.SimpleAction.new("about", None)
        action.connect("activate", self.on_about)
        self.add_action(action)

        action = Gio.SimpleAction.new("quit", None)
        action.connect("activate", self.on_quit)
        self.add_action(action)

        builder = Gtk.Builder.new_from_string(MENU_XML, -1)
        self.set_app_menu(builder.get_object("app-menu"))

    def do_activate(self):
        # We only allow a single window and raise any existing ones
        if not self.window:
            # Windows are associated with the application
            # when the last one is closed the application shuts down
            self.window = AppWindow(application=self, title="Main Window")

        self.window.present()

    def do_command_line(self, command_line):
        options = command_line.get_options_dict()

        if options.contains("test"):
            # This is printed on the main instance
            print("Test argument recieved")

        self.activate()
        return 0

    def on_about(self, action, param):
        about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True)
        about_dialog.present()

    def on_quit(self, action, param):
        self.quit()

if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)

¶ T0>

注意

Gtk.Table has been deprecated since GTK+ version 3.4 and should not be used in newly-written code. 改为使用Grid类。

表格允许我们将小部件放置在类似于Gtk.Grid的网格中。

网格的尺寸需要在Gtk.Table构造函数中指定。 要将一个小部件放入一个框中,请使用Gtk.Table.attach()

Gtk.Table.set_row_spacing()Gtk.Table.set_col_spacing()设置指定行或列的行之间的间距。 请注意,对于列,空间位于列的右侧,对于行,空间位于行的下方。

您还可以使用Gtk.Table.set_row_spacings()Gtk.Table.set_col_spacings()为所有行和/或列设置一致的间距。 请注意,通过这些调用,最后一行和最后一列不会有任何间距。

自3.4版以来已弃用: 据推测您可以使用Gtk.Grid作为新代码。

实施例¶ T0>

_images/layout_table_example.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
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class TableWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Table Example")

        table = Gtk.Table(3, 3, True)
        self.add(table)

        button1 = Gtk.Button(label="Button 1")
        button2 = Gtk.Button(label="Button 2")
        button3 = Gtk.Button(label="Button 3")
        button4 = Gtk.Button(label="Button 4")
        button5 = Gtk.Button(label="Button 5")
        button6 = Gtk.Button(label="Button 6")

        table.attach(button1, 0, 1, 0, 1)
        table.attach(button2, 1, 3, 0, 1)
        table.attach(button3, 0, 1, 1, 3)
        table.attach(button4, 1, 3, 1, 2)
        table.attach(button5, 1, 2, 2, 3)
        table.attach(button6, 2, 3, 2, 3)

win = TableWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

索引和表