全部博文(1159)
分类: C/C++
2011-11-17 16:32:17
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+ 中定义的。 稍后我们会看到类似 Glade XML 以 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 文件, 在 part2 中我们会用 C 语言调用 Libglade 库来解析这个 XML 文件并在运行时生成 UI 。 XML 文件很容易用 Python 应用程序或其它任何语言来解析。 Glade 能在修改过程中自动保存到该文件。退出文本编辑器,回到 Glade 我们继续。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(译者注: Mar 16 2009 , Glade3.6.0 released 。此版本及以后版本可以直接使用 xml 文件并且自动保存所做的修改)
///////////// Glade3.6.0 released 版本及以后版本,不用下面的操作
[root@localhost ~]# gtk-builder-convert tutorial.glade tutorial.xml
Traceback (most recent call last):
File "/usr/bin/gtk-builder-convert", line 772, in
sys.exit(main(sys.argv))
File "/usr/bin/gtk-builder-convert", line 760, in main
conv.parse_file(input_filename)
File "/usr/bin/gtk-builder-convert", line 161, in parse_file
self._parse()
File "/usr/bin/gtk-builder-convert", line 233, in _parse
assert glade_iface, ("Badly formed XML, there is "
AssertionError: Badly formed XML, there is no
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Manipulating Widget Properties
现在, Glade 的 Editor 区显示的是一个空的 Gtk Window 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"
列下点击灰色文本
"
到这里, 我们有了一个 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
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;
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
[root@localhost ~]# ./tutorial
(tutorial:3642): Gtk-CRITICAL **: gtk_widget_show: assertion `GTK_IS_WIDGET (widget)' failed
++++++++++++++++++解决方法
I'm looking at the tutorial code and the only line that could cause this error is:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 图标。在灰色的空窗口区点击,就放置了一个 Gtk VBox ,此时弹出一个对话框询问 "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 中选择 Gtk VBox 并设置其 "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 过程中的更多问题,可以询问
或
.
What Next?
在 中将选择一个确定的编程语言来实现我们刚刚创建的 GUI 。