原文链接:
Micah Carrick
www.micahcarrick.com/12-24-2007/gtk-glade-tutorial-part-1.html
Quick Overview of GTK+ Concepts
如果你没有任何GTK+ 的编程经验, 那么, 对于我将要阐述的一些概念你也许会听着犯迷糊。不过,不用担心, 在遇到这些概念的时候我会详细讲解,以便你能很好的阅读后面的内容。学完这一部分, 对GTK+ 的基本概念有所了解后, 你也许就能有效的利用Glade进行开发了。
首先, GTK+ 并不是一门编程语言, 而是一个开发工具套件, 或者说是一个开发库, 用来进行跨平台GUI应用程序的开发,Linux, OSX, Windows或其它任何平台都能使用GTK+。GTK+ 就好比Windows上的MFC 和Win32 API, JAVA 上的Swing和SWT, 或者Qt(KDE 使用的Linux下GUI开发套件)。
尽管 GTK+ 是用纯C语言编写的, 但是提供了其它各种语言的捆绑, 允许程序员选择自己喜欢的开发语言来开发 GTK+ 应用程序, 比如 C++, Python, Perl, PHP, Ruby等等。
GTK+ 开发套件基于三个主要的库: Glib, Pango, 和 ATK,当然我们只需关心如何使用GTK+ 即可, GTK+ 自己负责与这三个库打交道。 Glib 封装了大部分可移植的 C 库函数(允许你的代码移植到 Windows 和 Linux 上运行)。使用 C 或 C++ 时,将大量使用 Glib 库函数, 在我们用 C 语言的具体实现过程中我会详细解释它们。高级语言如 Python 和 Ruby 却不用担心 Glib 的使用, 因为它们有自己的标准库提供了相应的功能。
GTK+ 及相关的库时按照面向对象设计思想来实现的, 至于这时如何实现的现在并不重要, 不同的编程语言有不同的实现方法, 重要的是要知道 GTK+ 使用面向对象编程技术即可(是的, 即使是 C 实现的)。
每一个 GTK+ 的GUI元素都是由一个或许多个 “widgets”对象构成的。 所有的widgets都从基类GtkWidget派生。例如, 应用程序的主窗口是GtkWindow类widget, 窗口的工具条是GtkToolbar类widget。 一个GtkWindow是一个GtkWidget, 但一个GtkWidget兵不是一个GtkWindow, 子类widgets 继承自父类并扩展了父类的功能而成为一个新类, 这就是标准的面向对象 编程OOP(Object Oriented Programming)思想。
我们可以查阅GTK+参考手册找到widgets直接的继承关系。 对于GtkWindow它的继承链看起来像这样:
GObject
+----GInitiallyUnowned
+----GtkObject
+----GtkWidget
+----GtkContainer
+----GtkBin
+----GtkWindow
因此, GtkWindow继承自GtkBin, GtkBin继承自GtkContainer, 等等。在第一个程序中,你不需要担心GtkWidget对象。 各widget之间的继承链之所以重要是因为当你查找某个widget的函数, 属性和信号时, 你应该知道它的父类的函数,属性和信号也被此widget继承了,可以直接使用。在第二部分讲述此实例的代码时, 你能更清楚的认识到这一点。
我们来看命名规则, 命名规则带来的好处是非常便于使用。我们能够清楚 的看出对象或函数是哪个库中的。以Gtk开头的所有对象都是在GTK+中定义的。 稍后我们会看到类似GladeXML以Glade开头的是Libglade库对象或函数, GError以G开头的在GLib库定义。所有Widgets类都遵循标准camelcase命名习惯。所有操作函数都以下划线组合小写字母单词命名。如gtk_window_set_title()设置GtkWindow对象的标题属性。
你需要的所有参考文档都可以从以下网站获得:library.gnome.org/devel/references,
不过, 使用Devhelp更方便, 它甚至可以作为一个包来分发。 Devhelp可以浏览或搜索任何安装在你系统上的库的相关文档, 当然前提是你必须安装了这些文档。
Introduction to Glade3
Glade 是一种开发GTK+ 应用程序的 RAD(Rapid Application Development)工具。 Glade自身就是一个GTK+应用程序,因为它就是用GTK+ 开发出来的^_^ Glade用来简化 UI 控件的设计和布局操作, 进行快速开发。(译者注:当然,还不仅如此, Glade的设计初衷是把界面设计与应用程序代码相分离, 界面的修改不会影响到应用程序代码)Glade设计的界面保存为 glade格式文件,它实际上是一种XML文件。
Glade 起先能根据创建的 GUI 自动生成 C 语言代码(你仍然能找到此类相关的实例), 后来可以利用 Libglade库在运行时动态创建界面, 到了Glade3 ,这些方法都不赞成使用了。这是因为, Glade需要做的唯一的事就是生成一个描述如何创建GUI的glade文件。这给编程人员提供了更多的灵活性和弹性, 避免了用户界面部分微小的改变就要重新编译整个应用程序, 同时其语言无关性, 几乎所有的编程语言都可以使用Glade。
Glade3 进行了重新设计, 与之前的版本如 Glade2 有巨大的改变。2006年Glade3.0发布, 你可以自由获取最新版本进行开发。软件包管理器如 aptitude等应该都有Glade3的安装包, 不过请注意:有个数字 3, 因为"glade"是老版本的Glade2,"Glade-3" 或"Glade3"才是新版本。 你也可以从glade.gnome.org下载。
Getting Familiar with the Glade Interface
启动Glade3, 让我们来看看其主界面:
左边的是"Palette" 就像是一个图形编辑程序, 可以用它上面的GtkWidgets来设计你的用户界面。中间部分(刚启动时是空白一片)是"Editor" 所见即所得的编辑器。在右边, 上部是"Inspector", 下部是widget "Properties" 。Inspector以树形显示当前创建的控件的布局, 可以对控件进行选择。我们通过 Properties中各项内容来设置widgets的属性, 包括设置widgets的信号回调函数。
我们先创建一个顶层窗口并保存。点击Palette上"Toplevels"分组框中的 GtkWindow图标, 你会看到一个灰色窗口出现在Glade中间的 Editor 区域。这是GtkWindow的工作区:
窗口管理器(如GNOME)会自动加上窗口标题, 关闭按钮等, 因此我们编辑时看不见。使用Glade时, 我们总是需要首先创建一个顶层窗口,典型的是创建一个GtkWindow。
以 "tutorial.glade" 文件名保存工程。这个文件是一个XML文件, 你可以在文本编辑器中打开它:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.0 on Tue Nov 20 14:05:37 2007 -->
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<placeholder/>
</child>
</widget>
</glade-interface>
你看,这就是一个简单的XML文件, 在part2 中我们会用C语言调用Libglade库来解析这个XML文件并在运行时生成 UI 。XML文件很容易用Python应用程序或其它任何语言来解析。Glade能在修改过程中自动保存到该文件。退出文本编辑器,回到Glade我们继续。
Manipulating Widget Properties
现在,Glade的Editor区显示的是一个空的 GtkWindow widget。我们来修改它的属性。在Properties面板, 你会看到4个选项卡:'General', 'Packing', 'Common', 和 'Signals'。我们先来谈谈前面的两个。
GtkWidgets有许多属性,这些属性定义了它们的功能和现实方式。
如果你查阅一下GTK+的开发参考文档, 找到GtkWidget的"Properties"一项, 列出了GtkWindow的特有属性, 这些在Glade属性面板的"General"选项卡中, 并且每个widget的属性都会不一样。widget属性名称是我们的应用程序直接获取的信息, 把此GtkWindow的"name"由"window1" 修改为"window"。添加"GTK+ Text Editor"到"Window Title"属性:
我们稍后讲述 "Packing", 先来看看"Common", 这里也包括属性设置, 不过我们不能在开发人员参考文档中相应的widget属性下看到它们,这是因为这些属性是继承自父类的属性。在参考文档的"Object Hierarchy"里你将会看到GtkWindow的父类GtkContainer, 连接到GtkContainer属性项你将会看到一个"border-width", 而在Glade的属性面板中GtkWindow继承了这个属性, 你可以在"Common"选项卡底部找到。我们以后会讲到GtkContainer, 到这里, 你应该清楚地知道对象继承链是多么重要了。因为大部分widgets都从GtkContainer继承, 因此Glade把它的属性放到了"Common" Tab下。
参考文档的"Object Hierarchy", GtkContainer由GtkWidget继承。链接到GtkWidget, 你会看到其大部分的属性都列在了Glade属性面板的"Common" tab中。这些属性是所有GTK+ widgets的公共属性, 因为它们都继承自GtkWidget。
Specifying Callback Functions for Signals
当某些对程序员有意义的事情发生时, 控件对象就发出一个信号"signal"。这同Visual Basic中的"events"类似。当用户与界面进行交互时, 界面元素发出相应的信号, 程序员可以决定哪些信号需要捕获并连接到一个回调函数,完成某些任务。
我们遇到的第一个信号,也是你在所有GTK+应用程序中都会碰到的,是由GtkObject发出的"destroy"信号。当一个GtkObject对象销毁时就发出"destroy"信号。这非常重要,因为当用户通过点击一个widget顶部的"X"来关闭时, widget就销毁了。我们需要捕获这个信号并正确地退出我们的应用程序。在我们正式为此GUI写代码时做这件事是最好的, 不过先得在Glade中指定响应"destroy"信号的具体函数。
切换到属性面板的"Signals"tab,你将看到一个树形列表,显示了当前widget及其父类对象的所有信号。 这些与参考文档相符。
在"Handler"列下点击灰色文本"<Type here>"并开始编辑它, 从下拉列表框中选择"on_window_destroy"并按回车键。我们也可以键入任何名字, 不过Glade提供的下拉框列出了通用的回调函数习惯命名。这个值如何使用得看程序员如何连接信号与回调函数。在这里, 我们把GtkWindow的"destroy"信号连接到"on_window_destroy"函数名上。在part2中我们会看到这一点的。
到这里, 我们有了一个GUI, 可以编写代码显示我们的空窗口并在点击了关闭按钮时退出程序, 你可以用C, Python或任何其它语言。在此向导中, 我将会充分地向你展示如何在编写任何代码前就用Glade3建立 起完整的GUI. 不过, 为了满足你的好奇心, 同时也让你了解到要实现这个Glade用户接口,代码将会是多么的简单,请看代码:
In C
/*
First run tutorial.glade through gtk-builder-convert with this command:
gtk-builder-convert tutorial.glade tutorial.xml
Then save this file as main.c and compile it using this command
(those are backticks, not single quotes):
gcc -Wall -g -o tutorial main.c `pkg-config --cflags --libs gtk+-2.0` -export-dynamic
Then execute it using:
./tutorial
*/
#include <gtk/gtk.h>
void
on_window_destroy (GtkObject *object, gpointer user_data)
{
gtk_main_quit ();
}
int
main (int argc, char *argv[])
{
GtkBuilder *builder;
GtkWidget *window;
gtk_init (&argc, &argv);
builder = gtk_builder_new ();
gtk_builder_add_from_file (builder, "tutorial.xml", NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_builder_connect_signals (builder, NULL);
g_object_unref (G_OBJECT (builder));
gtk_widget_show (window);
gtk_main ();
return 0;
}
In Python (note: you must set the 'visible' property of 'window' to "Yes" in the 'Common' properties tab in Glade)
#!/usr/bin/env python
# First run tutorial.glade through gtk-builder-convert with this command:
# gtk-builder-convert tutorial.glade tutorial.xml
# Then save this file as tutorial.py and make it executable using this command:
# chmod a+x tutorial.py
# And execute it:
# ./tutorial.py
import pygtk
pygtk.require("2.0")
import gtk
class TutorialApp(object):
def __init__(self):
builder = gtk.Builder()
builder.add_from_file("tutorial.xml")
builder.connect_signals({ "on_window_destroy" : gtk.main_quit })
self.window = builder.get_object("window")
self.window.show()
if __name__ == "__main__":
app = TutorialApp()
gtk.main()
在这部分,我将不会深入讲解这些实现代码, 而把注意力放在Glade3上。不过你已经看到了, 实现一个Glade创建的用户接口是多么的简单。
Adding Widgets to the GtkWindow
查阅参考文档你会看到GtkWindow继承自GtkContainer。继承自GtkContainer的widgets就是一个容器widgets, 也就是说它们可以容纳其它的widgets。这是GTK+编程的一个基本理念。如果你是一个Windows程序员,你会期望着拖一堆的widgets到窗口上,并摆放好它们的位置即可。不过GTK+并不是这样工作地----有更好的理由。
GTK+的widgets可以装填到不同的容器, 容器能装填到其它的容器中。有不同的装填属性设置可以控制widgets在容器内如何分配空间,这样我们就可以创建出十分复杂的GUI界面, 而不用写任何代码来调整widgets大小尺寸和位置。因为GTK+为我们做了这一切。
不过这对于一个GTK+程序员新手来说也许是一个难以理解的概念,让我们用事实来说话!
GtkWindow继承自GtkBin容器,GtkBin是只能容纳一个子widget的容器,但是我们的界面需要3个元素:菜单条, 文本编辑区, 状态栏。因此, 我们使用GtkVBox,它可以容纳一个以上的子widgets, 并按照垂直排列。(GtkHBox按照水平排列子widgets)注:这里的"子widgets"是指容器中容纳的属于此容器的widgets
在 Palette面板上"Container"分组框下的 GtkVBox 图标上点击。此时"Select"工具条按钮弹起,并且鼠标在Glade编辑区上显示为带有 "+" 的 GtkVBox 图标。在灰色的空窗口区点击,就放置了一个GtkVBox,此时弹出一个对话框询问"Number of items", 设置GtkVBox的行数,我们选择3行。
编辑区的GtkWindow现在有三行。Glade窗口顶部的"Select"工具栏图标转换到按下状态,即允许你在编辑区选择任意的widgets。
接下来添加一个GtkMenuBar到GtkVBox的最顶上一行,GtkMeneBar在Glade的"Container"分组框下
现在,找到"Container"下的GtkScrolledWindow并添加到中间一行。完成这一步后除了中间一行被选中状态外看不出有什么变化。这是因为GtkScrolledWindow没有任何初始化可视元素。它仅仅是一个容器,当它容纳的子widgets变得太大时它提供滚动条。我们的编辑器需要滚动条支持。
点击"Control and Display"分组框下的GtkTextView并添加到GtkScrolledWindow上(中间一行)。
最后,点击"Control and Display"分组框下的GtkStatusbar并添加到最底部一行。
这就建好了我们文本编辑器的UI布局。在Inspector中你会看到widgets的包容关系。
在Inspector中选择widgets是很方便的,因为当widgets相互重叠时你在编辑区不能选择了。比如你不能在编辑区中点击GtkScrolledWindow因为我们只能看到它包容的子widgets, 你只能在Inspector中选择。
之前我提到装填的概念对一个GTK+程序员新手来说不好理解。因此,我将向你展示不同的装填方式是如何影响你的布局设计的。
How Packing Effects the Layout
从上面的界面设计过程,你也许会惊叹Glade如此的智能。它是如何知道我们不想状态栏太高?如果你调整窗口大小, 它又是如何知道应该让文本编辑框自动缩放来填充窗口变化的空间?哈哈, Glade靠猜的!它应用了默认设置,我们通常需要如此,不过不总是这样。
了解装填的最好方式是试验各种不同的装填属性,观察Glade如何响应。
你应该了解的关于装填和空间分配:
homogeneous:此属性设置,则告诉GTK+为每个子widgets分配同样大小的空间。
expand:子widgets的装填属性,确定在容器增长时,此子widgets是否获得额外的空间。
fill:子widgets装填属性,确定子widgets是否利用分配到的额外空间。
来看默认装填属性,GtkScrolledWindow的"expand"=TRUE表示当其所在容器增长时它要获得空间分配,"fill"=TRUE表示它将扩充自己来利用额外空间。这是我们想要的效果。
Widget |
Property |
Value |
GtkVBox "vbox1" |
homogeneous |
FALSE |
GtkMenuBar "menubar1" |
expand |
FALSE |
|
fill |
TRUE |
GtkScrolledWindow "scrolledwindow1" |
expand |
TRUE |
|
fill |
TRUE |
GtkStatusbar "statusbar1" |
expand |
FALSE |
|
fill |
TRUE |
现在我们来看homogeneous都干嘛了。在Inspector中选择GtkVBox并设置其"Homogeneous"="Yes",这表示"vbox1"将分配同样大小的空间给其包含的子widgets。既然其3个子widgets的"fill"=TRUE,那么它们将填满分配到的空间。
现在设置GtkScrolledWindow的"Expand"=Yes, "Fill"=NO。此时额外空间分配给GtkScrolledWindow,但是它并不扩充自己来利用分配到的额外空间。
我知道你认为这看起来很奇特,不过随着你深入了解Glade, 你将会清楚地知道装填属性是怎么工作的,并且你会惊叹为了改变GUI各元素位置和尺寸竟然只需要做如此少的工作就能完成。
Editing the Menu (or Toolbar)
Glade3拥有一个新的菜单条和工具条编辑器,尽管我们的向导里不使用GtkToolBar,不过它的处理与GtkMenuBar很相似。我们将利用Glade3的菜单编辑器来删除不需要的菜单项并对需要使用的菜单项设置信号处理。
你可以使用Glade的Inspector来删除,可以在Glade的属性面板中设置信号处理,不过Glade3的菜单编辑器更容易做这些工作。
在编辑区或通过Inspector选择GtkMenuBar,单击右键选择"Edit"启动菜单编辑器。
菜单编辑器包括的属性设置很像Glade主界面属性面板,而底部的信号设置与Glade属性面板中的"Signals"tab很像。只不过菜单编辑器中左边是树形列表显示。可以很轻易地添加或删除菜单项。移除"_View"菜单项。我们的向导中不使用这个菜单。对剩余的菜单项,我们需要重新命名,以便于在源代码中能清楚明了地引用它们。每个菜单项修改都一样,所以我只讲"New",记住,所有这些菜单编辑器能做的工作在Inspector和属性面板中也能完成。
Final Touches to the Main Window
对于"textview1" , "textview2"这种命名的引用十分不利于在源代码中使用,因此需要重新修改以下widgets的名称(记住,名称在属性面板的"General"tab)
1 "textview1"改为"text_view"
2 "statusbar1"改为"statusbar"
为了使其看上去更漂亮一些,我们为GtkScrolledWindow增加阴影和边框
1 在属性面板的"General"tab中把scrolledwindow1的"Shadow Type"改为"Etched in"
2 属性面板的"Common"tab中把scrolledwindow1的"Border Width"改为1
3 在"General"属性中把"text_view"的"Left Margin"设为2
4 在"General"属性中把"text_view"的"Right Margin"设为2
Getting Additional Help Using Glade
对使用Glade过程中的更多问题,可以询问
glade-users mailing list 或
GTK+ Forums.
What Next?
在 GTK+ and Glade3 GUI Programming Tutorial - Part 2 中将选择一个确定的编程语言来实现我们刚刚创建的GUI。