16.2 GTK+简介

了解了X视窗系统之后,下面我们该介绍GTK+工具包了。GTK+一开始是作为流行的GNU图像处理程序GIMP的一部分产生的,这也是GTK得名的原因(Gimp ToolKit的缩写)。因为GTK+已经发展并逐渐成为功能最强大和最受欢迎的工具包之一,所以GIMP程序设计者颇有远见地将GTK+变为一个独立的项目。GTK+项目的主页是http://www.gtk.org。

简而言之,GTK+是一个函数库,它提供了一组已制作好的被称为构件的组件。你通过简单易用的函数调用把这些组件和应用程序逻辑组合在一起,从而极大地简化了GUI的创建。

尽管GTK+是一个与GIMP一样的GNU项目,但是它使用的是更自由的LGPL许可证(Lesser General Public License)。LGPL允许人们使用GTK+来编写软件(包括源代码不开放的私有软件)而不用支付任何使用费、版税及受到其他限制。GTK+许可证所提供的自由度与它的竞争者Qt(下一章的主题)恰成对比,后者的GPL许可证禁止使用Qt开发商业软件(你必须购买一个商业Qt许可证)。

GTK+完全是用C语言编写的,而且绝大多数GTK+软件也是用C语言编写的。但幸运的是,有许多语言绑定使你可以在自己偏好的语言中使用GTK+,这些语言包括C++、Python、PHP、Ruby、Perl、C#和Java。

GTK+本身是建立在一组其他函数库之上的,如下所示。

❑ GLib:提供底层数据结构、类型、线程支持、事件循环和动态加载。

❑ GObject:使用C语言而不是C++语言实现了一个面向对象系统。

❑ Pango:支持文本渲染和布局。

❑ ATK:用来创建可访问应用程序,并允许用户使用屏幕阅读器和其他协助工具来运行你的应用程序。

❑ GDK(GIMP绘图工具包):在Xlib之上处理底层图形渲染。

❑ GdkPixbuf:在GTK+程序中帮助处理图像。

❑ Xlib:在Linux和UNIX系统上提供底层图形。

16.2.1 GLib类型系统

如果你阅读过GTK+代码,你可能会很奇怪为什么代码中有许多以字母g开头的C语言数据类型,如gint、gchar、gshort,还有一些像gint32和gpointer这样不熟悉的类型。这是因为GTK+建立在一个可移植的C语言库Glib和GObject之上,它们定义了这些类型来实现跨平台开发。

Glib和GObject提供了一组数据类型、函数和宏的标准替代集来进行内存管理和处理常见任务,从而实现跨平台开发。这些数据类型、函数和宏意味着作为GTK+程序员,我们可以确信我们的代码能可靠地移植到其他平台和体系结构上。

Glib还定义了一些方便的常量:

16.2 GTK+简介 - 图1

这些附加的数据类型基本上是标准C语言数据类型的替代(为了一致性和可读性),以及用于确保跨平台字节长度不变。

❑ gint、guint、gchar、guchar、glong、gulong、gfloat和gdouble是标准C语言数据类型的简单替代(为一致性考虑)。

❑ gpointer与(void *)同义。

❑ gboolean用于表示布尔类型的值,它是对int的一个包装。

❑ gint8、guint8、gintl6、guintl6、gint32和guint32是保证字节长度的有符号和无符号类型。

使用Glib和GObject几乎是透明的,这一点很有用。Glib在GTK+中被广泛地使用,因此,如果你有一个可以正常工作的GTK+,你将发现GLib也被安装了。在使用GTK+编程时,你甚至不需要明确地包含头文件glib.h,这一点你将在本章后面看到。

16.2.2 GTK+对象系统

编过GUI程序的人都能理解,我们为什么说GUI库非常适合于使用面向对象编程的范型,以至于所有的现代工具包(包括GTK+)都是以一种面向对象的风格编写的。

尽管GTK+是完全用C语言编写的,但是它通过GObject库支持对象和面向对象编程。这个库通过宏来支持对象继承和多态。

让我们看一个继承和多态的例子,它取自GTK+API文档中的GtkWindow的对象层次结构:

16.2 GTK+简介 - 图2

这个对象列表表明GtkWindow是GtkBin的一个子类,因此所有带GtkBin参数的函数在调用时都可以带GtkWindow参数。同样地,GtkBin继承自GtkContainer,而后者继承自GtkWidget。

为方便起见,所有构件创建函数都返回一个GtkWidget的类型。例如:

16.2 GTK+简介 - 图3

假设你创建了一个GtkWindow,并想把返回值传给某个需要以GtkContainer作为参数的函数(如gtk_container_add):

16.2 GTK+简介 - 图4

你需要使用宏GTK_CONTAINER在GtkWidget和GtkContainer之间进行类型转换:

16.2 GTK+简介 - 图5

后面将讲解这些函数的作用。现在你只需知道宏是经常被使用的,每一种可能的类型转换都有对应的宏存在。

如果你还不是很清楚,请不要担心。掌握GNOME/GTK+并不需要你理解面向对象编程的所有细节。事实上,利用C语言的知识背景就可以让你轻松学习面向对象编程思想和其优点。

16.2.3 GNOME简介

GNOME是一项1997年启动的项目的名称,该项目由GIMP程序员发起,目标是为Linux创建一个统一的桌面。人们有一个普遍的共识:缺乏一个一致的策略阻碍了Linux用作桌面平台的进程。那时,Linux桌面就像拓荒前的美国西部一样,没有整体的标准和约定的做法,却有“无所不为”的程序员精神。由于没有一个权威组织对桌面菜单、一致的界面外观、文档、翻译等进行控制,所以说好听点,Linux桌面的新手会感到很迷惑,说难听点,这样的桌面根本无法使用。

GNOME团队着手创建一个完全遵循GPL许可证的Linux桌面,用统一和一致的风格来开发工具和配置程序,同时促进程序间通信、打印、会话管理、GUI程序设计最佳实践等方面标准的制定。

他们努力的结果是显而易见的——现在GNOME已成为Fedora、Red Hat, Ubuntu以及openSUSE等发行版的默认Linux桌面的基础(见图16-1)。

16.2 GTK+简介 - 图6

图 16-1

GNOME最初代表的是GNU Network Object Model Environment(GNU网络对象模型环境),这反映了GNOME早期的一个目标,即为Linux引入一个像Microsoft OLE一样的对象框架,这样你就可以在字处理文档中嵌入电子表格了。现在,GNOME的设计目标发生了变化,我们所知道的GNOME指的是整个桌面环境,它包括一个启动应用程序的面板、一套程序和实用工具、编程库以及开发者支持特性。

在开始编程之前,你需要确认所有库都已安装好。

16.2.4 安装GNOME/GTK+开发库

一个带有标准应用程序和GNOME/GTK+开发库的完整GNOME桌面包括60多个软件包。因此,从头安装GNOME,无论是手工安装还是从源代码安装,都是一个令人畏惧的过程。幸运的是,现代Linux发行版都有很优秀的软件包管理工具,它使得安装GNOME/GTK+及其开发库变得轻而易举。

在Red Hat与Fedora Linux中,你通过点击应用程序菜单按钮(在左上角),并选择Add/Remove Software(增加/删除软件)来打开软件包管理工具。软件包管理工具打开后(见图16-2),请确认GNOME Software Development(GNOME软件开发)检查框被选中。它在Development(开发)区域中。

16.2 GTK+简介 - 图7

图 16-2

本章中,你将使用GNOME/GTK+2,因此请确认你安装了2.x版本的库。

对使用RPM包的发行版来说,你至少要安装如下的RPM包:

16.2 GTK+简介 - 图8

在本例中,文件名中的fc7指的是Fedora 7 Linux发行版。你的系统显示的名字可能稍微不同。

在Debian或基于Debian的系统(如Ubuntu)中,你可以使用apt-get命令从各种镜像站点中安装GNOME/GTK+。更多细节请访问http://www.gnome.org。

另外,你也可以尝试运行一下GTK+的演示程序,它们展示了所有构件的外观(见图16-3):

16.2 GTK+简介 - 图9

16.2 GTK+简介 - 图10

图 16-3

对每个构件,你都可以看到一个Info标签和一个Source标签。后者显示了使用指定构件的实际C语言源代码。这里提供了大量的示例。

实 验 一个空白的GtkWindow

让我们以一个最简单的GUI程序来开始GTK+编程吧,它用于显示一个窗口。你将看到GTK+库的实际使用情况,并看到你可以从很少的代码中获得多少功能。

(1)输入程序的代码,并把它保存为gtk1.c:

16.2 GTK+简介 - 图11

(2)为编译gtk1.c,请输入:

$ gcc gtk1.c-o gtk1 pkg-config —cflags —libs gtk+-2.0

注意,输入的是反引号,而不是单引号。请记住反引号是要求shell执行其包含的命令并将输出结果附加其后。

当使用以下命令来运行这个程序时,你的窗口将会弹出(见图16-4):

16.2 GTK+简介 - 图12

图 16-4

16.2 GTK+简介 - 图13

注意,你可以对这个窗口进行移动、调整大小、最小化和最大化。

实验解析

你用一条语句#include <gtk/gtk.h>来包含必需的GTK+库和相关库(包括Glib)的头文件。接着,你声明窗口为一个指向GtkWidget的指针。

为了初始化GTK+库,你必须调用gtk_init函数,将命令行参数argc和argv传递给它。这给了GTK+一个机会来解析它需要知道的任何命令行参数。注意:你必须在调用任何GTK+函数之前对其进行这样的初始化。

这个例子的核心代码是对gtk_window_new的调用,其函数原型是:

GtkWidget* gtk_window_new (GtkWindowType type);

参数type根据窗口的目的可取下面两个值之一。

❑ GTK_WINDOW_TOPLEVEL:一个标准的有框架窗口。

❑ GTK_WINDOW_POPUP:一个适用于对话框的无框架窗口。

你几乎总是使用GTK_WINDOW_TOPLEVEL,因为你将在后面看到,还有更方便的创建对话框的方法。

gtk_window_new调用在内存中建立窗口,使得在将窗口实际显示在屏幕之前,你可以在它里面放置构件,调整它的大小,改变窗口的标题等。要实际显示窗口,你需要调用gtk_widget_show:

gtk_widget_show(window)

该函数只需要一个GtkWidget指针,因此你只需把窗口的引用传给它即可。

你最后调用的函数是gtk_main。这个关键函数通过把控制权交给GTK+来启动交互过程,并且一直运行,直到调用gtk_main_quit才返回。正如你所看到的,gtk1.c并未调用gtk_main_quit,因此,即使窗口被关闭,程序也不会停止。你可以试着点击关闭图标,你将看到并没有返回命令提示符。我们将在下一节学过信号和回调函数之后再来纠正这个错误。至于现在,你可以在启动gtkl程序的shell窗口中按下Ctrl+C组合键来退出这个程序。