原文連結:
Micah Carrick
www.micahcarrick.com/12-24-2007/gtk -glade-tutorial -part -1.html
Part 1
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 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 區顯示的是一個空的 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" 列下點選灰色文本 "<Type here>" 并開始編輯它, 從下拉清單框中選擇 "on_window_destroy" 并按Enter鍵。我們也可以鍵入任何名字, 不過 Glade 提供的下拉框列出了通用的回調函數習慣命名。這個值如何使用得看程式員如何連接配接信号與回調函數。在這裡, 我們把 GtkWindow 的 "destroy" 信号連接配接到 "on_window_destroy" 函數名上。在 part2 中我們會看到這一點的。
到這裡, 我們有了一個 GUI , 可以編寫代碼顯示我們的空視窗并在點選了關閉按鈕時退出程式, 你可以用 C , Python 或任何其它語言。在此向導中, 我将會充分地向你展示如何在編寫任何代碼前就用 Glade3 建立 起完整的 GUI. 不過, 為了滿足你的好奇心, 同時也讓你了解到要實作這個 Glade 使用者接口,代碼将會是多麼的簡單,請看代碼:
In C
#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 圖示。在灰色的空視窗區點選,就放置了一個 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 過程中的更多問題,可以詢問
glade-users mailing list 或
GTK+ Forums .
What Next?
在 GTK+ and Glade3 GUI Programming Tutorial - Part 2 中将選擇一個确定的程式設計語言來實作我們剛剛建立的 GUI 。