GTK+ 基础,第 2 部分: 如何使用 GTK+

html-css013

GTK+ 基础,第 2 部分: 如何使用 GTK+,第1张

本文假设读者熟悉基本的面向对象概念,比如类、对象、方法和继承。虽然不需要能够用 C 编写程序,但是需要对 C 语言的语法有基本的理解。C 语言的 GTK+ 应用程序剖析我发现最好用示例来讨论代码。对于本文来说,我使用一个用 C 编写的名为 Hello World 的简短应用程序。虽然短小 —— 而且作为应用程序来说,基本没什么用 —— 但是这个应用程序的代码确实展示了在进行 GTK+ 编程时可能会碰到的一些最有趣的概念(参见清单 1)。清单1. Hello World 应用程序的 GTK+ 代码 #include <gtk/gtk.h>#include <libintl.h>#define _(x) gettext (x) #define N_(x) (x) #define GETTEXT_PACKAGE "gtk-hello" #define LOCALEDIR "mo" static char *greetings[] = { "Hello World", "Witaj ?wiecie", "世界に今日は" }static char* choose_greeting () { return greetings[g_random_int_range (0, G_N_ELEMENTS (greetings))]} static void cb_button_click(GtkButton *button, gpointer data) { GtkWidget *label = GTK_WIDGET(data)g_assert(label != NULL)gtk_label_set_text(GTK_LABEL (label), choose_greeting())} static gboolean cb_delete(GtkWidget *window, gpointer data) { gtk_main_quit()return FALSE} int main (int argc, char *argv[]) { GtkWidget* window, *button, *label, *vboxbindtextdomain (GETTEXT_PACKAGE, LOCALEDIR)bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8")textdomain (GETTEXT_PACKAGE)gtk_init(&argc, &argv)window = gtk_window_new (GTK_WINDOW_TOPLEVEL)button = gtk_button_new_with_label (_("Hello World"))label = gtk_label_new (choose_greeting())vbox = gtk_vbox_new(FALSE, 0)gtk_container_add(GTK_CONTAINER (window), vbox)gtk_container_add(GTK_CONTAINER (vbox), label)gtk_container_add(GTK_CONTAINER (vbox), button)g_signal_connect(G_OBJECT (window), "delete-event", G_CALLBACK(cb_delete), NULL)g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_button_click), label)gtk_widget_show_all(window)gtk_main()return 0}概述在进入细节之前,先看看运行 Hello World 程序时发生的情况:初始化 GTK+ 和国际化(i18n)支持。创建部件(widget)。部件被组织成层次结构,让 GTK+ 知道如何在屏幕上显示它们。两个信号处理器被连接起来 —— 一个用来在用户关闭窗口时退出应用程序,另一个用来在用户点击按钮时,修改显示的欢迎信息。在屏幕上显示窗口,应用程序调用 gtk_main(),激活主循环。主循环一直运行,直到用户关闭窗口,调用 gtk_main_quit() 时才结束。初始化以下几行初始化 GTK+ 和 i18n 支持:清单2. 初始化 GTK+ 和 i18n 支持 int main (int argc, char *argv[]) { GtkWidget* window, *button, *label, *vboxbindtextdomain (GETTEXT_PACKAGE, LOCALEDIR)bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8")textdomain (GETTEXT_PACKAGE)gtk_init(&argc, &argv)C 程序员应当熟悉 main 声明。(如果不是 C 程序员,那么只需要知道这是应用程序开始执行的函数。)下一行包含几个到 GtkWidget 类型的指针的声明。GTK+ 是一个面向对象的工具包。所以,它使用常见的面向对象概念(例如继承)来实现不同的部件。作为语言,C 缺乏对面向对象的内置支持。GTK+ 通过使用一些更聪明的技巧和 C 标准要求的一些有帮助的属性,克服了这个缺点。在这个方案中,对象由指针代表,而 GtkWidget 是GTK+ 层次结构中的基本类型 —— 叫做 类,其他类都从它派生而来。所以,我把变量声明为 GtkWidget*。下三行是在程序开始的地方应当包含的调用,用来得到国际化接口的支持。注意,在真实的应用程序中,不会手动声明 LOCALEDIR 和GETTEXT_PACKAGE。编译系统将会处理这些声明。但是,在这个简单示例中,声明有助于澄清需要的内容。最后一行调用 gtk_init()。在进行其他 GTK+ 调用之前,必须调用这个函数,并把调用程序使用的参数传递给它。如果这个调用失败,就会从没有机会正确初始化自己的各个子系统得到多个错误消息。创建部件这四行分别调用不同的 _new() 函数:清单3. 调用不同的 _new() 函数 window = gtk_window_new (GTK_WINDOW_TOPLEVEL)button = gtk_button_new_with_label (_("Hello World"))label = gtk_label_new (choose_greeting())vbox = gtk_vbox_new(FALSE, 0)关于TOPLEVEL 的警告在看到 gtk_window_new 的TOPLEVEL 参数之后,可能想知道是否有其他可能的窗口类型。实际上,确实有。但是,对于 TOPLEVEL 之外的类型的需要很少见。而且,要使用不同的类型,必须对 GTK+ 与窗口系统的交互方式有很好的理解。所以,规则很简单:一直 使用TOPLEVEL 参数。正如您所想,这些函数创建新的部件。所以,它们是代表部件的对象的构造函数。在 C++ 中,用特殊的方式标记构造函数,并用特殊语法进行调用。但是,因为 C 不支持面向对象,所以它们与普通的函数没有什么不同。只有添加到函数名尾部的 _new() 才表示这些函数实际上是构造函数。规定是:gtk_* 名称空间中的每个构造函数都返回一个到 GtkWidget 的指针。这样,通过声明这种类型的变量,可以直接把构造函数调用的结果分配给对应的变量。如果查看单独的构造函数,可以看到它们接受不同的参数,对应着它们创建的部件的类型。具体来说,gtk_window_new (GTK_WINDOW_TOPLEVEL) 创建新的 TOPLEVEL 窗口;也就是与用户看成窗口的东西对应的部件,具有标题栏、关闭按钮或者窗口系统添加的其他元素。对label 和button 的构造函数的调用做的工作正如您所料。但是,请注意传递给 button 的字符串周围的下划线和括号(_())。这个宏调用 gettext() 例程,并且对于界面转换至关重要。(关于 gettext 的更多信息,请参阅 参考资料。) 模样古怪的 gtk_vbox_new(FALSE, 0) 创建垂直框(VBox)。虽然这个部件与屏幕上的任何可视像素都不对应,但是它在 GTK+ 的控件布局中扮演着重要角色,您很快就会看到这一点。 决定布局这三行决定部件的布局: gtk_container_add(GTK_CONTAINER (window), vbox)gtk_container_add(GTK_CONTAINER (vbox), label)gtk_container_add(GTK_CONTAINER (vbox), button)这些行是对类型 GtkContainer 的面向对象方法的调用。如果查看应用程序编程接口(API)参考,可以看到 GtkContainer 继承自 GtkWidget,而且它的全部方法都接受 GtkContainer* 作为第一个参数。所以,GtkContainer* 是方法要在上面操作的对象实例。因为变量是 GtkWidget* 类型的,而且 C 编译器不支持面向对象继承,所以需要让编译器相信:向需要 GtkContainer* 的函数传递这些变量是安全的。GTK_CONTAINER() 宏通过实现到类型安全版 GtkContainer 的类型转换做到了这一点。类型安全 意味着宏在进行类型转换之前,会验证指定操作可以在指定类型上安全地执行。如果宏不能执行指定操作,就会提供警告。因为GTK+ 使用方框布局 模型,所以不必显式地指定部件应当放在屏幕上的什么位置。相反,要指明部件放在其他哪个窗口部件内。在 Hello World 应用程序中,每个 gtk_container_add() 方法调用都告诉应用程序用第一个参数(或 双亲部件),并把第二个参数(或 子部件)放在双亲部件内。在这个示例中使用的 VBox 部件是一种布局部件,它垂直地排列子部件。这样,在它内部放置标签和按钮时,结果就是按钮显示在标签下。这是需要做的全部工作。如果您曾经用过绝对定位(在某些工具包中使用的模型,例如 Win32)手动调整部件或重新设置部件大小,那么您会很高兴知道在 GTK+ 中所有这些都是自动完成的。连接信号和主循环在创建并组织好部件之后,该给它们添加些逻辑了。GTK+ 像多数 GUI 工具包一样,是一个事件驱动框架。所以,它是围绕主循环 组织的。主循环在连续的检查-分配-睡眠周期上操作。当事件发生时,与这个事件对应的对象发出信号,通知主循环事件已经发生。然后主循环查询自己的信号和处理程序之间的内部映射表,也叫做 回调,并调用注册到指定对象的指定信号的处理程序。在Hello World 代码中,回调的注册看起来像这样:清单4. 回调的注册 g_signal_connect(G_OBJECT (window), "delete-event", G_CALLBACK(cb_delete), NULL)g_signal_connect (G_OBJECT(button), "clicked", G_CALLBACK(cb_button_click), label)请注意在 GTK+ 中,connect(连接) 到信号。第一行把 cb_delete 函数连接到 window 对象的 delete-event 信号。类似地,第二行把 cb_button_click 函数连接到 button 对象的 clicked 信号。请注意在第二个连接调用中的第四个参数 label。稍后会看到它在 cb_button_click 函数中是如何使用的。下面是 cb_delete 函数,它在用户关闭窗口时退出应用程序: static gboolean cb_delete(GtkWidget *window, gpointer data) { gtk_main_quit()return FALSE} static 修饰符 在C 中,static 关键字在内部链接函数,这意味着静态函数在声明它的源文件之外不可见。除非需要在不止一个文件中使用回调,否则请一直使用 static 关键字声明回调。使用这个方法,可以避免把可用函数名称的有限名称空间弄混乱。因为静态函数被局限在一个文件,所以可以安全地重用函数名称。 这个函数接受 GtkWidget* 和一个未指定的 data 指针(gpointer 是等价于 void* 的类型),因为 "delete-event" 的每个回调都必须符合这个原型。但是,这个函数不需要这些参数,所以它们被忽略了。它调用 gtk_main_quit() 例程以退出主循环。而且,该函数返回布尔值,因为为 GtkWidget 定义的 delete-event 信号的回调原型指明的是布尔返回值。布尔值决定 GTK+ 采取的行动。如果它返回 TRUE 值,那么事件就被认为已经得到处理,而且不调用默认处理程序(该默认处理程序从窗口系统删除部件)。有的时候,例如,如果想显示一条消息,请求尚未保存的数据,并根据用户的响应阻止窗口关闭,那么这个函数会很有用。下面是 cb_button_click 函数,在用户点击按钮时,它修改显示的欢迎信息:清单5. cb_button_click 函数 static void cb_button_click(GtkButton *button, gpointer data) { GtkWidget *label = GTK_WIDGET(data)g_assert(label != NULL)gtk_label_set_text(GTK_LABEL (label), choose_greeting())} 可以看到,这个函数与 cb_delete 函数类似,不同之处是它什么也不返回,而且它接受 GtkButton* 而不是 GtkWidget。代码把 data 转换成到 GtkLabel 的指针。还记得在回调注册中的 label 参数么?现在每次调用回调时,data 指针都会包含到那个标签的指针。每当需要向回调传递额外的信息时,都可以使用 data 参数。类似地,如果需要访问发出信号的对象,就要使用第一个参数,在这个具体回调中是 button。在得到标签的指针后,代码使用 g_assert 宏来确定标签是否等于 NULL。g_assert 宏是一个来自 Glib (GTK+ 使用的一个有用的 C 类型和例程库)的工具宏,如果传递给它的条件满足,就会中止程序 —— 在这个示例中,条件是 label 等于NULL。因为 label 等于NULL 意味着程序员犯了错误,所以这可以确保在代码投入生产之前捕获到错误。显示窗口在回调连接好之后,gtk_widget_show_all() 函数使窗口 —— 即所有的部件 —— 都显示在屏幕上(参见图 1)。图1. Hello World 应用程序,用波兰语和日语运行激活主循环当诸事就位,而且显示出来之后,gtk_main() 函数就激活主循环。主循环进入无限循环,等候事件并调用回调,直到有人关闭窗口,调用 gtk_main_quit() 为止。注意:如果对代码仍有问题,请参阅附带的源代码。它与文章中介绍的代码完全相同,但是每一行都包含详细的注释。编译和运行要编译这个程序,需要 C 编译器和 GTK+ 的开发文件(头和库)。有关如何获取这些项目的信息,请参阅 参考资料。在安装完文件之后,请解压源代码,进入源代码将要解压到的目录,并运行 make: $ tar -xzf gtk_hello.tgz $ cd gtk_hello $ make 注意:如果正在运行 Microsoft�0�3 Windows�0�3,请不要运行 make,而是打开 Microsoft Visual Studio�6�4.NET 并运行 “hello” 项目。回页首在其他编程语言中的 GTK+可以在多个编程语言中使用 GTK+。要做到这点,需要使用绑定。绑定 是针对指定语言的特殊包,它用适合该语言的方式公开 GTK+ API。例如,我已经把 Hello World 应用程序转换成 Python 和 C#。要在这些语言中运行 GTK+,除了 Python 和 Mono/.NET 之外,分别还需要 PyGTK 和 Gtk#(请参阅 参考资料)。PyGTK 中的 Hello World 清单6 显示了转换成 Python 的 Hello World 应用程序的代码。清单6. PyGTK 中的 Hello World 应用程序 import pygtk pygtk.require('2.0') import gtk import random greetings = ["Hello World", "Witaj ?wiecie", "世界に今日は"] def choose_greeting (greets): return greets[random.randint (0, len(greets) - 1)] def cb_clicked(button, label): label.set_text(choose_greeting(greetings)) window = gtk.Window () vbox = gtk.VBox () button = gtk.Button("Hello World") label = gtk.Label (choose_greeting (greetings)) window.add(vbox) vbox.add(label) vbox.pack_start(button, False, False) window.connect("delete-event", lambda a,b: gtk.main_quit()) button.connect("clicked", cb_clicked, label) window.show_all() gtk.main() 由于紧凑的 Python 代码,应用程序的这个版本比它的 C 语言对应物更短。除此之外,看起来是相似的。注意,代码被转换成使用 Python 的习惯,但是 API 保持不变。Gtk# 中的 Hello WorldGtk# 中的 Hello World 应用程序代码要比 C 版本略长,因为 C# 要求的声明很长。所以,我没有在这里包含完整的源代码。源代码包含在附加的下载中。下面快速查看一下 C 到 C# 的主要概念转换: class GtkHello : Gtk.Window { 现在不再创建并设置新窗口,而是把 Gtk.Window 类放进一个子类,并把所有设置代码移动到构造函数。这种方法并非特定于 Gtk#。实际上,在 C 程序中,当需要窗口的多个拷贝时,经常使用这种方法。但是,在 C# 中使用子类如此之容易,所以即使对一个实例这么做也有意义 —— 特别是在考虑到 C# 要求至少声明一个类的时候,更是如此。 this.DeleteEvent += new DeleteEventHandler(DeleteCB)button.Clicked += new EventHandler(ButtonClickCB)可以看到,GTK+ 信号被转换成地道的 C# 事件概念。名称也被稍加修改,以更好地符合 C# 的命名规范。清单7. GTK+ 信号被转换成地道的 C# 事件概念 private void DeleteCB (object o, DeleteEventArgs args) { Application.Quit ()args.RetVal = true} 由于C# 事件的构造方式,所以 delete-event 处理程序的原型略有不同。它不是从回调返回 true,而是通过 args.RetVal 传递返回值。gtk_main() 和gtk_main_quit() 分别被 Application.Run() 和Application.Quit() 代替。回页首有用的工具在使用 GTK+ 进行开发时,有几个工具可以让工作轻松些。其中最著名的几个工具是 Glade、Libglade 和 Devhelp。Glade 和 LibgladeGlade 是一个界面构建器 —— 这个程序可以图形化地构建应用程序,而不必手动从源代码开始构建。更重要的是第二个组件:Libglade。顾名思义,Libglade 支持读取可扩展标记语言(XML)格式,Glade 用 XML 保存用户界面描述。使用 Libglade,可以直接从这个描述构建应用程序的界面,而不需要任何代码。图2 显示了一个包含几个部件的简单的 Libglade 应用程序。图2. 简单的 Libglade 应用程序清单8 显示了图 2 所示的 Libglade 应用程序的完整源代码。清单8. Libglade 应用程序的源代码 #include <gtk/gtk.h>#include <glade/glade.h>int main (int argc, char *argv[]) { GladeXML *uigtk_init(&argc, &argv)/* Read user interface description from glade file */ ui = glade_xml_new ("glade_hello.glade", "main_window", NULL)/* Automatically connect signals */ glade_xml_signal_autoconnect(ui)gtk_main()return 0} 可以看到,所有的事情只有 17 行代码,包括注释和空行。虽然真正的应用程序不会这么短,但是从代码的清晰性、模块性和可管理性来说,Libglade 带来了巨大的提高。如果想进一步研究这个程序是如何构建的,可通过本文附带的下载中其余的示例进行了解。DevhelpDevhelp 是一个文档浏览器,是为了阅读用 gtk-doc 生成的格式的文档而设计的,gtk-doc 是构建 GTK+ 文档的标准工具,相关的项目,例如 Pango 和 GNOME 也使用它。使用 Devhelp,可以迅速地搜索函数索引并浏览已经安装的文档,从而可以更迅速地获得需要的信息。Devhelp 是一个 GNOME 应用程序。所以,要运行它,需要一个符合 POSIX 的操作系统,在上面运行 GNOME,例如 Linux�0�3 或 Sun Solaris,还需要 GNOME 运行时库。不需要运行 GNOME 本身。如果使用其他平台,也有许多其他方法可以阅读 GTK+ 文档。Mono 项目有 Monodoc 浏览器,它通常是与 Gtk# 参考一起预先装入的。GTK+ 的 Windows 安装器也通常包含适合各种开发工具的文档格式,例如 Visual Studio�0�3。最后,总有使用 Web 浏览器在线阅读文档这个选项,但是推荐采用专用浏览器,因为它的速度快,还有针对在程序文档中进行搜索而设计的额外特性。下期预报在这篇文章中,学习了 GTK+ 编程中使用的基本概念。还看到了如何在 C 语言之外的语言中使用 GTK+,在保持使用 GTK+ 的一般方式的同时,还使用了特定于这些语言的方式。最后,还介绍了有助于更快更好地开发应用程序的工具。在本系列的最后一期中,将进一步观察 GTK+ 开发的另一个方面:部署。文章将详细分析各种选项,包括移植性的考虑和安装的简易。最后,将以更广阔的视野来看 GTK+ —— 作为一个有活跃社区的项目,它有助于构建出更好地为用户服务的应用程序。回页首下载描述名字大小下载方法Source codeos-gtk2_hello.zip18KBHTTP关于下载方法的信息参考资料 学习您可以参阅本文在 developerWorks 全球站点上的 英文原文。请阅读 developerWorks “GTK+ 基础” 系列中的全部文章。GNU Gettext 是一个用于应用程序运行时转换的库。Libglade Reference Manual 是用来动态创建 GTK+ 界面的库。请访问 GTK+ 获得关于工具包的更多信息。全面的 GTK+ API Documentation 对于所有开发人员都很重要。Matthias Warkus 编写的 The Official GNOME 2 Developer's Guide(No Starch Press,2004 年)介绍了 GNOME 2,包括使用 GTK+ 进行编程。请访问 developerWorks 开放源码专区 获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发并把它们用于 IBM 的产品。获得产品和技术 请获取 Gtk#,这是针对 Microsoft .NET 环境的 GTK+ 绑定。请获取 GTK+ 的官方 source code tarballs。请访问 PyGTK,这是 Python 的 GTK+ 绑定的官方站点。Gazpacho 是Glade UI 描述文件的改进的编辑器,是用 PyGTK 编写的。Devhelp 是用于 GNOME 的一个面向程序员的文档浏览器。请参阅 GNOME,这是使用 GTK+ 构建的侧重于应用的桌面。请尝试 Xfce,这是一个快速而易用的桌面,也是用 GTK+ 开发的。请访问 Gnomefiles 并得到超过 1,000 个用 GTK+ 构建的应用程序。请用IBM 试用软件 改造您的下一个开放源码开发项目,这些软件可以下载也可以通过 DVD 得到。讨论 请在GTK+ mailing lists 上获得关于用 GTK+ 开发软件的支持并询问相关问题。通过参与 developerWorks blogs 加入developerWorks 社区。关于作者Maciej Katafiasz 是计算机科学专业的研究生,从高中起就一直使用开放源码技术。从 GNOME 1.0 起,他就是 GNOME 桌面的用户,而 2.0 版一发布,他就爱上了它并了解到 GTK+ 能够开发自己喜欢的桌面。关闭[x]关于报告滥用的帮助报告滥用谢谢! 此内容已经标识给管理员注意。关闭[x]关于报告滥用的帮助报告滥用报告滥用提交失败。 请稍后重试。关闭[x]developerWorks:登录IBM ID:需要一个 IBM ID?忘记IBM ID?密码:忘记密码?更改您的密码 保持登录。单击提交则表示您同意developerWorks 的条款和条件。 使用条款 当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。所有提交的信息确保安全。关闭[x]请选择您的昵称:当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。昵称:(长度在 3 至 31 个字符之间)单击提交则表示您同意developerWorks 的条款和条件。 使用条款. 所有提交的信息确保安全。为本文评分评论回页首

在上一篇中,我们讲述过 Gtk2-perl 的内部控件,并将大的分类简单介绍了一下。在这里,我们会详细的说明一下某些控件,让大家能较为清楚的了解这些控件。注意,每个控件内部都有很多的调用函数,如果要讲解每个控件的所有函数将要耗费大量的篇章,所以这里只是讲解一些基本的或者重要的函数。

我们先来讲一下这些控件的用途:

Gtk2-perl 中的控件有很多,但是无外乎这几种类型,一种是用来显示数据的,一种用来操作数据的,一种是用来将其他控件布局的,还有一种是特殊目的的控件。

1、显示数据的控件一般可以显示文字数据,图片数据或者其他特殊数据。这类控件的特点是主要用来显示数据,显示的数据一般是不可编辑或操作的。用户一般可以通过这类控件得到某些信息,但是不能操作这些控件。更具体的:

显示文字数据的控件我们一般使用 Gtk2::Label,这个控件主要是用来显示一小段文字从而表现一个标题或者一点提示的作用,也就是说是用来点缀程序的。

显示图片数据的控件我们一般是用Gtk2::Image,这个控件的目的就是用来显示一个图片,与上面的 Gtk2::Label 的作用基本相似。

我们还有显示进度条的控件Gtk2::ProgressBar,这个控件可以显示一个进度条,来指示用户当前某个程序的进展情况,在某些长时间运行的程序中我们经常可以见到。

Gtk2::Statusbar 显示一个状态栏。这个控件一般用在程序的底部,显示某些提示信息给用户。

Gtk2::Frame 一个装饰性的控件,显示一个外边框并带有一个标题。它的内部可以再嵌入一个子控件。

Gtk2::Hseparator 另一个装饰性的控件,显示一个一条水平分割线。

Gtk2::Vseparator 同上,显示一个垂直分割线。

Gtk2::List 一个列表形的控件。就是将数据以垂直排列的方式显示出来。经常用来罗列很多相似的数据。可以按照数据的值来排序显示。

Gtk2::TreeView 树形显示控件。主要用来以树形方式显示数据。例如:类似我们经常使用的win32下的资源管理器就是一个典型的树形控件的例子。在这个控件中你可以显示一个树形列表,可以展开或者收缩这个列表,可以编辑这个列表中的每一项,可以在这个列表中使用简单的按钮等等。这个控件通常与Gtk2::TreeStore 等其他控件一同使用。这个控件的概念也与操作类型的控件有些重复,它不仅显示,还可以让用户操作。

2、操作数据的控件一般可以让用户来输入或编辑文字,或者选择某个选项,或者触发某个事件,例如:点击按钮来运行某个函数。

Gtk2::Button 一个普通的按钮控件,当点击时我们可以触发某个事件。

Gtk2::CheckButton 一个check(打对勾)类型的按钮,一般有两种状态:选择或者没有选择。

Gtk2::RadioButton 一个radio(小圆点)类型的按钮,一般是多个radio类型的按钮,然后我们在其中选择一个,经常用于互斥选择。

Gtk2::ToggleButton 一个toggle(和开关类似)类型的按钮,按下后会保持按下的状态直到用户再次按下才显示按钮松开。

Gtk2::Entry 一个单行文字的输入框。用户可以在这里输入一行文字

Gtk2::TextView 强大的文字控件,不仅可以让用户输入、显示、编辑多行文字,而且可以显示静态图片或者简单的动画图片,甚至可以嵌入其他的控件,例如,嵌入按钮控件等等。这个控件的使用在后面我们会详细的讲述。

Gtk2::Combo 一个选择控件。很像html中的select下拉菜单,点击后出现一个下拉菜单,用户可以在下拉菜单的多个选项中选择一个选项。一般常用在工具栏中,例如:word中我们可以在工具栏里直接选择一个字体,或者字体的大小时,我们都会用到该类型的控件。

Gtk2::SpinButton 一个数字选择控件。这个控件主要用来在一定的范围内选择一个数字,控件的左边是上下选择的两个按钮,可以让用户将所选择的数字加1或减1,用户也可以直接在控件中输入自己需要的数字。

Gtk2::Hscale 和 Gtk2::Vscale 水平和竖直输入框。这个框水平或者竖直显示一个移动条,用户移动这个条就可以在一定的范围内选择一个中间值来作为输入的数据,不太常见。

3、将其他控件布局的控件一般并不明确的显示在用户的面前,它们只是负责将其他的控件在主窗口中摆放整齐。在这里一般有两种布局的思想,一种是使用 table 的方式将主窗口横竖的划分,然后将所需的控件按照自己的要求放入划分出来的每个单元中;另一种是使用画布式的方式,你可以随意的摆放所有的控件,只要你准确的说出每个控件在主窗口中的坐标就可以。

按照table方式布局的控件有:

Gtk2::HBox 一个用来水平分割的控件,目的是在不同水平上摆放多个控件。

Gtk2::VBox 一个竖直分割的控件。目的类上。

Gtk2::Table 一个按照表格的方式来分割的控件,也就是同时既有水平分割也有竖直分割,是上面两个控件的结合体。

按照画布方式布局的控件有:

Gtk2::Fixed 这个控件可以让你在它内部的任意位置放置固定的子控件。不过,这个控件有些缺点:例如可能会导致文字的被截断,或者多个子控件的重叠等显示问题。所以这里并不太推荐使用该控件。

Gtk2::Layout这个控件可以让你在它内部自由放置子控件,但是它支持滚动窗口,可以有无限的滚动区域。这个控件是为了弥补Gtk2::Fixed中的缺陷而编写的,它与Gtk2::Fixed的用法基本相同,因此推荐使用这个。

为了达到某些复杂布局的目的,很多时候布局控件都是混合使用的。不过,总的来说,按照table方式布局更加容易。

4、特殊目的的控件在Gtk2-perl中有很多,它们都有自己特殊的目的,而我也将大多数无法以上面的形式分类的控件都归结到这里了。

首先这样一类控件是选择类控件,这类控件的目的是跳出一个新窗口,来完成某个在很多程序中都通用的功能。

Gtk2::ColorSelection 颜色选择控件。跳出一个通用的颜色选择窗口,用户可以按照红、绿、蓝的方式来选择一个自己要求的颜色,用户还可以调节颜色的不透明度、饱和值、色调,然后返回用户所选择的颜色值。

Gtk2::ColorSelection图例:

Gtk2::FontSelection 字体选择控件。跳出一个通用的字体选择窗口(该窗口中可以选择的字体都是读取的当前系统所拥有的字体),用户可以选择字体,字体大小以及样式,并伴有当前选择字体的一个预览。

Gtk2::FontSelection图例:

Gtk2::FileSelection 文件选择控件。跳出一个文件选择窗口,可以让用户单选或多选文件,还可以选择目录、建立文件或建立目录。用户可以自由的更换目录,不过这个文件选择框与windows平台的文件选择框的样式不同,应该说它们都有各自的优势吧。

Gtk2::FileSelection图例:

Gtk2::Calendar 日历控件。显示一个标准的公历日历,用户可以查看当前的年、月、日,或者选择一个当前的日期。

Gtk2::Dialog 一个简单的对话框,上面可以有几个简单的按钮选择,例如:确定、取消、应用。这个控件一般用来处理某些用户的简单选择,例如:提示用户程序出错,提示用户是否存储文件等等。

其次,菜单类的控件。这类控件都是用来在程序的顶部显示一个通常意义上的菜单或者工具栏。

Gtk2::Menu 一个标准的菜单控件。这个控件可以让我们产生下拉菜单并在这个下拉菜单中包含多个菜单选项或者更深的级联菜单。每个菜单项可以对应于一个触发事件。菜单上可以添加图标,还可以添加快捷键。

Gtk2::Toolbar 标准的工具栏控件。我们都清楚,现在的GUI程序一般在菜单下面会有一个快捷工具栏,这个工具栏内放入一些特别常用的按钮,来方便用户的迅速使用。Gtk2::Toolbar 就是这样的标准工具栏。它允许插入文字、图标按钮。

再次,还有一些控件的目的就更加特殊了。

Gtk2::Scrollbar 滚动窗口控件。这个控件相当的重要,很多时候如果我们要显示的内容在程序的主窗口中无法完全显示,我们就需要加入这个控件,更准确的说是将显示数据的控件加入到 Gtk2::Scrollbar 控件中,这样我们就可以有水平与垂直的滚动条来滚动显示多出窗口的数据。

Gtk2::DrawingArea 相当重要的控件,可以称之为绘图控件。这个控件可以帮助我们建立自己的控件或者是不使用任何现有的控件直接调用底层的绘图指令。我们都很清楚,现有的控件也许可以建立标准的一个GUI程序,但是我们在写GUI程序的时候,很有可能有自己的特殊需求,需要的控件并不能在标准控件中找到,这时我们就需要用Gtk2::DrawingArea。要使用该控件就必须了解Glib、pango等模块。在下一篇中我们将讲解这个控件的使用。

Gtk2::Tooltips 提示控件。这个控件专门用来对于GUI界面上的一些简单标签做更详细的解释,例如:当我们将鼠标停放在界面的某个标签上时,过一会可以出现一个文字框来对于这个标签做更详细解释。

Gtk2::Notebook 纪事本控件。这个控件也是相当强大,就像我们在firefox里最常使用的tab窗口一样,我们可以通过这个控件来在一个主窗口中嵌入多个控件,而每个控件通过控件上方的标签来切换显示。微软在IE7中也采用了类似的控件。

基本上,上述的控件就是我认为在 Gtk2-perl 编程中经常要使用到的,还有一些较为偏僻的控件,在这里就暂不介绍了。下面,我们来针对几个较为重要的控件详细的说明一下。

回页首

二.Gtk2::Window

这个可以说是最重要的控件了, Gtk2::Window 决定了你所显示的主窗口的样式以及相应的一系列设置。

我们这里通过代码来说明:

my $win = Gtk2::Window ->new ( $type ) ## 建立一个新的窗口对象

$win ->set_title ( "Test for window" ) ## 设置这个窗口的标题

$win ->set_default_icon_from_file ( $filename )## 设置窗口的小图标

$win->set_size_request ( "800" , "600" ) ## 设置这个窗口的大小

$win ->set_position ( 'center' ) ## 设置这个窗口的位置

$win ->set_type_hint ( $hint )## 设置这个窗口暗示类型

$win ->signal_connect ( 'destroy' , sub { Gtk2->main_quit } ) ## 设置窗口退出时程序退出

一个新窗口的图例:

第一行里面的 $type 值可以为:toplevel 或 popup。 toplevel 代表了这个窗口是最顶层的窗口,这个窗口可以包含其他的窗口,是我们最常用的窗口类型;popup代表这个窗口只是弹出窗口,这个窗口一般是菜单窗口或者工具提示窗口,通常就是一个弹出式的提示窗口。缺省的话就是toplevel。

第二行,我们可以设置窗口的标题,注意这里的标题文字必须是 utf8 编码,所以当你输入中文时一般需要转换编码。我们可以用$win ->get_title 来取回当前窗口的标题,例如:当我们想要在当前窗口标题的基础上添加某些文字时就常用这个。

第三行,我们设置了一个图标,这个图标是直接从硬盘上的文件取出,文件的格式可以为 png、jpg、gif、bmp,图标的大小一般为16x16像素,如果太大则只会裁去16x16范围以外的部分。

第四行,我们设置了窗口大小,我们还可以固定窗口的大小:$win ->set_resizable ( 0 )或者随时改变窗口的大小:$win ->resize ( $width , $height )或者取回当前窗口大小:( $w,$h ) = $win ->get_size。当然,我们还可以将窗口全屏显示:$win ->fullscreen或者取消全屏:$win ->unfullscreen最大化也是如此:$win ->maximize$win ->unmaximize。

第五行,我们设置的是窗口刚被创建时所出现的位置。这里可以有:none、center、center-always、center-on-parent、mouse。None的话代表随机出现,mouse的话代表窗口的右上角出现在鼠标指针的位置上。

第六行,我们设置窗口的暗示类型。可以设置的值有:normal、dialog、menu、toolbar、splashscreen、utility、dock、desktop。

Splashscreen可以称之为闪屏,就是我们在启动程序时常见的一个显示窗口,随着程序的启动完成这个窗口也会随之消失而变为正式的程序操作窗口,这样的窗口在现代GUI程序中十分常见,主要是为了在用户等待程序的全部读入时给用户一个较好的印象。这个Splashscreen 窗口没有最大最小化、关闭的按钮。

Desktop称为桌面。也就是说这个窗口会作为一个桌面出现,并没有任何的标题或者最大最小化、关闭等按钮。在这里,必须强调一点:win32与X11的不同。例如:在win32环境下Desktop 暗示就无法起作用,它只能作用在X11环境中。所以当你试图编写一个跨平台运行的程序时,就要避免使用这种选项。

dock 船坞选项。这个选项是使窗口作为一个小面板来出现的。该选项同样只能在X11环境中使用。具体的使用可以参考x11的内部协议规定。

注意:在win32下很多暗示选项是不起作用的。

这里还要介绍一个函数:$win ->set_decorated,它的作用是将窗口的装饰去掉,也就是去掉窗口的标题、最大化最小化关闭按钮所在的那一行,窗口作为一个裸窗口显示。当我们需要在win32下使用这种窗口时,可以先设置一个特殊的窗口暗示类型,然后再$win ->set_decorated (0)就可以。

也许大家都会有疑问,我们为什么要使用这些特殊的暗示类型。在x11下,大家可以参考x11的内部协议规定,里面有描述。在win32下,我想主要是要使用裸窗口。裸窗口的使用在各个平台下还是十分常见的,例如:我们常见的msn信件到来通知,就是这样一个窗口;或者当我们需要写一个比较怪异的窗口时,例如:windows media player里面的那些怪异窗口,这时我们就需要去掉原有的标准窗口装饰,并自己的用图片描绘出窗口的样式。

除了以上我们介绍的这些关于窗口的api以外,还有$win ->present设置窗口显示在用户面前;$win ->set_gravity ( $gravity )这个也是用来设置窗口最初出现时的位置,$gravity可以为north-west、north、east等。

回页首

三.Gtk2::TextView

强大的多行文字控件,可以显示太多种的数据,基本上如果你是想要纯粹的显示数据,或者编辑文字与图片组成的数据流,都可以直接使用这个控件来完成。

这个控件包含几个部分:

i. Gtk2::TextBuffer

ii. Gtk2::TextChildAnchor

iii. Gtk2::TextIter

iv. Gtk2::TextMark

v. Gtk2::TextTag

vi. Gtk2::TextTagTable

vii. Gtk2::TextView

Gtk2::TextBuffer 的目的是保存所有要显示的数据在一个文字缓冲中。

Gtk2::TextChildAnchor 的目的是在文字缓冲中插入一个控件。

Gtk2::TextIter 的目的是操作当前缓冲所处的位置。

Gtk2::TextMark 的目的是在文字缓冲中保存一个位置,比较像我们在Html中的书签功能,我们可以随意的回到这个位置。

Gtk2::TextTag 的目的是为文字缓冲中的文字添加一个显示标签,例如:改变文字的显示颜色,改变文字的字体,或者文字大小。

Gtk2::TextTagTable 的目的是保存所有 Gtk2::TextTag 标签。每个 Gtk2::TextBuffer 缓冲只对应于一个Gtk2::TextTagTable 。

Gtk2::TextView 的目的是显示文字缓冲中的数据。

我们看到了,要实现一个多行文字控件,需要多个模块配合起来使用,我们大概来描述一下这个过程:一个Gtk2::TextView 控件,它的显示数据(文字与图片)是要存储在Gtk2::TextBuffer 建立的缓冲中,而这个缓冲中的数据我们可以为其添加Gtk2::TextTag ,从而为缓冲数据(文字)设定显示格式,或者我们用 Gtk2::TextChildAnchor 可以在缓冲中添加其他的控件来显示。当我们想要操纵我们在缓冲中的位置时,我们可以使用 Gtk2::TextIter ,例如:向前移动一行,向后移动一个字母。当我们想要在缓冲中标记一个位置,然后可以快速的移动到这个位置去显示,我们可以使用 Gtk2::TextMark 。

OK,我们现在来个实例来说明一下:

my $sw = Gtk2::ScrolledWindow ->new ()

$sw ->set_policy ( 'automatic' , 'automatic' )

my $textview = Gtk2::TextView ->new ()

$textview ->can_focus (1)

$textview ->set_editable (0)

$textview ->set_left_margin (10)

$textview ->set_right_margin (10)

$textview ->set_wrap_mode ( 'GTK_WRAP_WORD_CHAR' )

my $tab_table = Gtk2::TextTagTable ->new ()

my $text_buffer = Gtk2::TextBuffer ->new ( $tab_table )

$textview ->set_buffer ($text_buffer )

$sw ->add ( $textview )

一个textview控件的实例:

这个实例里我们首先建立了一个 Gtk2::ScrolledWindow 控件,这个控件的目的是为了产生滚动条。当我们要显示的数据比 Gtk2::TextView 的窗口大时,我们就需要滚动条。这里我们设置$sw ->set_policy ( 'automatic' , 'automatic' )代表了水平滚动条与垂直滚动条都是自动(automatic)出现的,也就是如果要显示的数据超出了窗口,这才出现滚动条,否则将不显示滚动条。还有一点要注意的是:$sw ->add ( $textview )滚动条控件要包含文字控件。

然后,我们建立了一个 Gtk2::TextView 控件,并设置它的属性。

$textview ->can_focus (1)表示这个文字控件可以接受输入。

$textview ->set_editable (0)表示这个文字控件的内容不能被编辑,也就是说我们不能随意的插入文字。如果设置为1,那么我们就可以编辑文字控件的内容,就像一个记事本那样。

$textview ->set_left_margin (10)设置文字控件左面的空白大小为10像素,这个空白是说该控件与窗口边界(或者另一个控件)之间的距离,主要是美观的需要。

$textview ->set_wrap_mode ( 'GTK_WRAP_WORD_CHAR' )设置文字换行方式为遇到窗口边界就自动换行。其他的选项还有:none,不自动换行;

然后,我们又建立了一个 Gtk2::TextTagTable 表。接着建立了一个Gtk2::TextBuffer 并将该Gtk2::TextTagTable 表赋予文字缓冲。(这里其实有更简单的方式,我们可以直接使用Gtk2::TextBuffer ->new (),这样会自动建立一个tagtable。)

最后,我们将 Gtk2::TextView 的文字缓冲设置为刚建立的那个缓冲。至此,初始化的工作我们就全部完成了。接下来,我们要来看看如何向这样一个文字控件中插入数据。

看例子:

use Gtk2::Pango

$text_buffer ->set_text ( "" )

my $iter = $text_buffer ->get_iter_at_offset (0)

$text_buffer ->insert_txt ($iter , "test test test\n" )

$text_buffer ->create_tag ( "bold" , weight =>PANGO_WEIGHT_BOLD )

$text_buffer ->insert_with_tags_by_name ( $iter , "test test" , "bold" )

my $pixbuf = Gtk2::Gdk::Pixbuf ->new_from_file ( 'test.jpg' )

$text_buffer ->insert_pixbuf ( $iter , $pixbuf )

my $input = Gtk2::Entry ->new

my $anchor = $text_buffer ->create_child_anchor ( $iter )

$textview ->add_child_at_anchor ( $input , $anchor )

$textview ->show_all ()

首先,我们 use Gtk2::Pango,这是为了使用下面的 PANGO_WEIGHT_BOLD 这样的常量定义。

$text_buffer ->set_text ( "" )先将缓冲清空。my $iter = $text_buffer ->get_iter_at_offset (0)将我们的iter移动到缓冲的开头处。这两步都是一个文字缓冲开始插入数据前最好执行的两步,因为一般一个文字缓冲都要重复的使用,所以在开始一次新的使用前做一下这些工作可以保证缓冲的正确运行。

这里要说明一下iter。iter这里代表的是一个在缓冲中的当前位置,当插入任何数据时,都要从这个iter的当前位置开始插入。由于可以在缓冲中插入文字、图片、控件等多种数据,所以iter的计算是由gtk+的内部完成的,我们只需要关心如何使用iter就可以了。

接下来,$text_buffer ->insert_txt ($iter , "test test test\n" )我们插入一段文字:"test test test\n"。这里还有点说明:这里的$iter值当插入完成时会被自动的增加到插入的结尾处,以便于下一次数据的插入。所以,下一次插入数据时我们还可以直接使用 $text_buffer ->insert_txt ($iter , "test test test\n" )就可以了,而不用管$iter值,因为每次插入后$iter值都被自动更新了。

接着,我们 $text_buffer ->create_tag ( "bold" , weight =>PANGO_WEIGHT_BOLD )建立了一个粗体的显示标签,将这个标签的名称标为:"bold"。这里我们可以创建的标签有很多种,例如:文字的颜色,字体,大小,风格,粗体,斜体,或者文字的下滑线,文字背景色,文字的背景位图,文字的对齐方式等等。

$text_buffer ->insert_with_tags_by_name ($iter , "test test" , "bold" )我们插入一段文字" test test",并将标签名称为"bold"的标签赋给这段文字,这样这段文字就会以粗体的方式显示。

接下来,my $pixbuf = Gtk2::Gdk::Pixbuf ->new_from_file ( 'test.jpg' )我们读取了一个图像文件,建立了一个新的pixbuf。$text_buffer ->insert_pixbuf ( $iter , $pixbuf )我们将这个图片插入到了文字缓冲中。

最后,my $input = Gtk2::Entry ->new建立了一个新的单行文字输入控件。 my $anchor = $text_buffer ->create_child_anchor ( $iter )在缓冲的当前位置下建立了一个子锚。 $textview ->add_child_at_anchor ( $input , $anchor )在$textview 文字显示控件中添加新建立的文字输入控件到子锚上。 $textview ->show_all ()显示所有的数据。当在缓冲中插入一个控件时,最好调用一下,否则控件可能会不显示出来。

Gtk2::TextView文字控件的功能很强,使用起来也比较的方便,所以可以用来实现一些较为常规的功能,如:编写一个文字编辑器,一个简单的显示软件等等。笔者的perl web browser项目"shellweb"最初就是使用这个控件来显示html数据的。如果大家感兴趣的话可以参考其中的用法。