16.5 GTK+构件

在本节中,我们将介绍应用程序中最常用的一些GTK+构件的API。

16.5.1 GtkWindow

GtkWindow是所有GTK+应用程序的基本元素。我们用它来持有构件:

16.5 GTK+构件 - 图1

有许多的GtkWindow API调用,下面这些是值得特别关注的:

16.5 GTK+构件 - 图2

正如你刚才所看到的,gtk_window_new在内存中创建了一个新的空白窗口。窗口的标题没有设置,窗口的大小和屏幕位置都没有定义。你通常会将各种构件填入其中,并设置一个菜单和工具栏,然后才调用gtk_widget_show在屏幕上显示它。

gtk_window_set_title函数通过向窗口管理器发出请求来改变标题栏文本。

注意:因为是窗口管理器而不是GTK+负责绘制窗口周边,所以文字字体、颜色和大小都取决于你所选的窗口管理器。

gtk_window_set_position控制窗口在屏幕上的初始位置。参数position有5个取值,如表16-2所示。

表 16-2

16.5 GTK+构件 - 图3

gtk_window_set_default_size按GTK+绘图单元设置屏幕中窗口的大小。明确设置屏幕大小可以避免窗口内容不清楚或被隐藏。一旦窗口在屏幕上显示,你可以通过gtk_window_resize来强制调整窗口大小。默认情况下,用户可以以通常的方法通过拖曳窗口边框来改变其大小。要阻止用户这么做,你可以调用gtk_window_set_resizable函数,将参数resizable设为FALSE。

为了确保窗口在屏幕上并且对用户是可见的,即它没有被最小化或隐藏,你可以使用gtk_window_present来完成这个任务。gtk_window_present对于对话框来说很有用,它可以确保在你需要用户输入时它们没有被最小化。另外,要强制最大化和最小化窗口,你可以使用gtk_window_maximize和gtk_window_minimize函数。

16.5.2 GtkEntry

GtkEntry是一个单行文本输入构件,它常用于输入简单的文本信息,例如电子邮件地址、用户名或主机名。你可以通过相应的API调用来设置和读取输入的文本,设置允许的最大字符数,以及设置其他一些属性来控制文本的定位和选择:

16.5 GTK+构件 - 图4

GtkEntry可被设置为使用星号(或者任何其他用户定义的字符)来代替输入的字符,这在输入密码时很有用,因为你不希望别人在旁边看到你输入的文本。

下面我们将描述最有用的一些GtkEntry函数:

16.5 GTK+构件 - 图5

你可以通过gtk_entry_new或固定最大文本输入长度的gtk_entry_new_with_max_length来创建一个GtkEntry。限制输入不超过某一长度将省去你检查输入长度,并通知用户文本输入过长的负担。

要获取GtkEntry的内容,你可以调用gtk_entry_get_text函数,它将返回GtkEntry内部的一个const char指针(G_CONST_RETURN是一个GLib定义的宏)。如果你想修改这个文本或把它传给一个可能修改它的函数,就必须使用像strcpy这样的函数来复制这个字符串。

你可以通过_set_text、_append_text、_modift_text函数来手工设置或修改GtkEntry的内容。注意这些函数都使用const指针作为参数。

如果想将GtkEntry作为一个密码输入框使用,在显示字符的地方用星号来代替,你可以用gtk_entry_set_visibility函数,并将它的参数visible设为FALSE。不可见字符的替代符号可以根据需要使用gtk_entry_set_invisible_char函数来改变。

实 验 用户名和密码输入框

你已经了解了GtkEntry函数,现在通过一个小程序来实际演示它们。entry.c将创建一个用户名和密码输入窗口,然后将输入的密码与一个内置的密码相比较。

(1)首先定义这个内置的密码,就选为secret吧:

16.5 GTK+构件 - 图6

(2)下面是两个回调函数,分别在关闭窗口和点击OK按钮时调用:

16.5 GTK+构件 - 图7

(3)在main函数中,创建和排列界面,并且连接好回调函数。我们用hbox和vbox容器构件来放置标签和输入框构件。

16.5 GTK+构件 - 图8

16.5 GTK+构件 - 图9

运行这序,你会看到如图16-8所示的窗口。

16.5 GTK+构件 - 图10

图 16-8

实验解析

这个程序创建了两个GtkEntry构件(username_entry和password_entry),并将password_entry的可见性设为FALSE来隐藏输入的密码。然后它创建了一个GtkButton,并将它的“clicked”信号连接到button_clicked回调函数。

在回调函数中,程序将获取输入的密码,并将它与内置的密码做比较,然后显示适当的信息。

注意,我们多次使用gtk_box_pack_start语句来向容器中增加构件。为了减少这些重复的代码,你将在后面的例子中定义一个辅助函数。

16.5.3 GtkSpinButton

有时候,你需要用户输入一个数字类型的值,例如一个设备的最大速度或长度。在这种情况下,使用GtkSpinButton是最理想的。GtkSpinButton限制用户只能输入数字字符,你可以为输入值设置上界和下界。这个构件还提供向上和向下的箭头,用户仅用鼠标就可以很方便地选择数值。

16.5 GTK+构件 - 图11

相应的API函数都很简单明了,下面我们列出最常用的函数:

16.5 GTK+构件 - 图12

要使用gtk_spin_button_new来创建一个GtkSpinButton,你首先需要创建一个GtkAdjust-ment对象。GtkAdjustment是一个抽象对象,它包含控制有界数值的逻辑。GtkAdjustment也在其他构件中使用,如GtkHScale和GtkVScale。

要创建GtkAdjustment,你需要给它传递一个初始值、上界、下界和递增量:

16.5 GTK+构件 - 图13

step_increment和page_increment的值分别设置最小和最大递增量。在使用GtkSpinButton的情况下,step_increment设置点击箭头时值变化的量。page_increment和page_size对于GtkSpinButton来说不重要。

gtk_spin_button_new的第二个参数climb_rate用于控制当你持续按着箭头按钮时数值变化的快慢。最后,参数digits设置构件的精度。因此,当digit值为3时,spin按钮将显示0.00。

gtk_spin_button_new_with_range可以很方便地在创建GtkSpinButton的同时创建一个GtkAdjustment,你只需传递给它上下界和递增量即可。

使用gtk_spin_button_get_value可以很容易地读取到当前值。如果希望获得一个整数值,你可以使用gtk_spin_button_get_value_as_int。

实 验 GtkSpinButton

你将通过一个小例子看到GtkSpinButton的实际使用情况。这个程序名为spin.c。

16.5 GTK+构件 - 图14

运行这个程序,你会得到一个数值在50〜100之间的微调(spin)按钮(见图16-9)。

16.5 GTK+构件 - 图15

图 16-9

16.5.4 GtkButton

你已在程序中看过GtkButton的使用情况,但是从GtkButton还派生出很多按钮构件,它们有着更丰富的功能,非常值得一提:

16.5 GTK+构件 - 图16

从上面的构件层次图中可以看到,GtkToggleButton直接继承自GtkButton, GtkCheckButton继承自GtkToggleButton, GtkRadioButton继承自GtkCheckButton。每个子构件都有其专门用处。

1.GtkToggleButton

GtkToggleButton和GtkButton几乎完全一样,但它们之间有一个重要区别:前者拥有状态。也就是说,它可以打开或关闭。当用户点击GtkToggleButton时,它按通常的方式发出“clicked”信号,并改变(或切换)其状态。

GtkToggleButton的API函数非常简单明了:

16.5 GTK+构件 - 图17

两个值得关注的函数是gtk_toggle_button_get_active和gtk_toggle_button_set_active,你调用它们来读取和设置开关按钮的状态。一个TRUE返回值表明GtkToggleButton处于“开”状态。

2.GtkCheckButton

GtkCheckButton是一个变相的GtkToggleButton。它不像GtkToggleButton那样显示一个令人厌烦的矩形方块,而是显示一个旁边带有文本的复选框,这看起来颇令人兴奋,但两者之间并没有功能上的差异。

16.5 GTK+构件 - 图18

3.GtkRadioButton

这个按钮与前面的有很大不同,因为它可以与同类型的其他按钮分为一组。GtkRadioButton是这样一种按钮,它允许你一次只能从一组选项中选择一个。它的名字来源于老式的收音机——它们有许多机械按键,当你按下一个按键时,其他的按键都将弹起来。

16.5 GTK+构件 - 图19

RadioButton(单选按钮)组由一个Glib的列表对象GSList表示。为了将单选按钮放在一个组里,你可以创键一个GSlist,并通过gtk_radio_button_new和gtk_radio_button_get_group来将它传递给每一个按钮。不过,还有一个更简单的方法,通过gtk_radio_button_new_from_widget可以从一个现有的按钮中获取GSList。你将在下一个例子中看到它的使用方法。在下面的例子中,我们将使用不同的GtkButton。

实 验 GtkCheckButton、GtkToggleButton和GtkRadioButton

输入下面这个程序,并把它命名为buttons.c。

(1)首先将按钮指针声明为全局变量:

16.5 GTK+构件 - 图20

(2)这里我们定义了一个辅助函数,它将GtkWidget和GtkLabel放入一个GtkHbox中,然后将这个GtkHbox添加到一个指定的容器构件中。这样做有助于减少重复的代码。

16.5 GTK+构件 - 图21

(3)print_active是另一个辅助函数,它以一个描述字符串的形式输出给定GtkToggleButton的当前状态。它在button_clicked函数中被调用,该函数是一个与OK按钮的clicked信号相连接的回调函数。每次这个按钮被点击时,你都将获得一个按钮状态的输出信息。

16.5 GTK+构件 - 图22

(4)在main函数中,你创建按钮构件,将它们放在一个GtkVbox中并加上描述标签,然后将回调信号连接到OK按钮:

16.5 GTK+构件 - 图23

图16-10显示了buttons.c程序的运行情况,里面有4种常见类型的GtkButton。

16.5 GTK+构件 - 图24

图 16-10

点击OK可以看到各种按钮的状态。

这个程序是一个简单的示例,它使用了4种类型的GtkButton,并显示了你如何通过一个单独的函数gtk_toggle_button_get_active来读取GtkToggleButton、GtkCheckButton和GtkRadio-Button的状态。这是面向对象方法最大的好处之一,因为你不需要为每个button类型准备单独的 get_active函数,从而减少了代码量。

16.5.5 GtkTreeView

至此,我们已看到了一些简单的GTK+构件,但并不是所有的构件都是单行输入或显示的。GtkWidget的复杂性也没有受到任何限制,GtkTreeView就是一个很好的例子,它封装了大量的功能:

16.5 GTK+构件 - 图25

GtkTreeView是GTK+2新增的构件族的一部分,它可以创建电子表格或文件管理器中常见的数据的树和列表视图。通过GtkTreeView,你可以创建数据、混合文本、位图、甚至是使用GtkEntry构件输入的数据等的复杂视图。

测试GtkTreeView最快速的方法就是运行GTK+自带的gtk-demo应用程序。这个演示程序展示了包括GtkTreeView在内的各种GTK+构件的能力,如图16-11所示。

16.5 GTK+构件 - 图26

图 16-11

GtkTreeView构件族由下面4个组件构成。

❑ GtkTreeView:树和列表视图

❑ GtkTreeViewColumn:代表一个列表或树的列

❑ GtkCellRenderer:控制绘图单元

❑ GtkTreeModel:代表树和列表数据

前3个组成了所谓的视图(view),最后一个是模型(model)。这种将视图和模型分开的概念(通常称为模型/视图/控制器设计模式,或简称为MVC)并不是GTK+专有的,而是一个在整个软件行业越来越受到青睐的设计模式。

MVC设计模式的主要优点是,数据可以同时由不同的视图展示,而不需要进行不必要的复制。例如,文本编辑器可以分不同的窗格来编辑文档的不同部分,而不需要在内存中保留文档的两个副本。

MVC模式在Web编程中也很受欢迎,这是因为它使得你可以轻松创建一个满足如下要求的Web站点:使用与桌面浏览器不同的方式在手机或WAP浏览器上展示内容。你只需针对每种浏览器类型开发独立优化的视图组件即可。你还可以将获取数据的逻辑(如查询数据库)与用户界面逻辑相分离。

我们先来介绍模型组件,GTK+有两个这样的组件:GtkTreeStore用于存储如目录层次结构这样的多级数据,GtkListStore用于存储平面数据。

为了创建一个GtkTreeStore,你需要传递一个列数,接着是每列的类型:

16.5 GTK+构件 - 图27

从store中读取、增加、编辑和删除数据就需要使用GtkTreeIter结构。这些迭代器结构指向树中的节点(或列表中的行),并对可能非常大的数据结构中的一部分进行定位和操纵。有好几个API函数可以获取树中不同位置的迭代器对象,但我们将只介绍最简单的gtk_tree_store_append。

在向树store中添加任何数据之前,你需要一个迭代器对象来指向一个新行。gtk_tree_store_append在树中填入一个GtrTreeIter对象,该对象代表一个新行,这个新行或是一个顶层行(如果你传递的第三个参数是NULL),或者是一个子行(如果你传递的第三个参数是父行的迭代器对象)。

16.5 GTK+构件 - 图28

一旦有了一个迭代对象,你就可以通过gtk_tree_store_set来填充该行:

16.5 GTK+构件 - 图29

你成对地传递列号和数据,以-1结束。你将在后面使用一个枚举类型来增加列号的可读性。

为给该行增加一个分支(一个子行),你只需通过再次调用gtk_tree_store_append,并传递一个顶层行来为子行创建一个迭代器对象:

16.5 GTK+构件 - 图30

你可以在API文档中找到更多GtkTreeStore和GtkListStore相关函数的资料。下面我们将介绍GtkTreeView视图组件。

创建GtkTreeView本身很简单,你只需要向构造函数传递GtkTreeStore或GtkListStore模型即可:

16.5 GTK+构件 - 图31

现在是配置构件让它准确显示数据的时候了。针对每列,你都必须定义一个GtkCellRenderer(渲染器对象)并设置数据源。例如,你可以选择只显示某些列或交换列的显示顺序。

GtkCellRenderer是一个用于处理在屏幕上绘制每个单元格的对象。它有3个子类,分别用于处理文本单元格、位图图形单元格和开关按钮单元格,如下所示:

❑ GtkCellRendererText;

❑ GtkCellRendererPixBuf;

❑ GtkCellRendererToggle。

你将在视图中使用文本渲染器GtkCellRendererText:

16.5 GTK+构件 - 图32

这里你创建了渲染器对象并将它传递给列插入函数。这个函数通过传递给它的以NULL结尾的键/值对,一次就设置好了GtkCellRendererText属性。传给函数的参数分别是树视图、列号、列标题、渲染器对象和渲染器属性。这里你设置了text属性,传递了数据源的列号。GtkCellRendererText还定义了其他几个属性,包括下划线、字体、大小等。

你将在下面的例子中看到GtkTreeView的实际使用情况。

实 验  GtkTreeView

输入这个程序,将它命名为tree.c.

(1)程序使用一个枚举类型来标记列,这样你就可以用名字来引用它们。N_COLUMNS是总列数。

16.5 GTK+构件 - 图33

(2)下面创建了树store,并向其传递列的总数和每列的类型:

16.5 GTK+构件 - 图34

(3)接下来向树中增加一个父行和一个子行:

16.5 GTK+构件 - 图35

(4)最后,把列加到视图中,并设置它们的数据源和标题:

16.5 GTK+构件 - 图36

后面会把GtkTreeView作为CD应用程序的核心,在该程序中查询CD数据库时,我们将修改GtkTreeView的代码。

我们已经了解了GTK+构件,现在我们将把注意力转向GNOME。后面我们将学习如何使用GNOME库向应用程序中添加菜单,以及GNOME构件是如何使得为GNOME桌面编程变得更容易。