- 论坛徽章:
- 0
|
試著想用單一篇幅就讓人能開始設計 Gtk+2 程式,就和希望人們在讀幾篇唐詩賞析後就能開始吟詩作對般不切實際。但由於這只是個人的筆記,就請不要對此吹毛求疵了。
Gtk+ [color="#9999ff"](GIMP Toolkit) 是 GNU 的專案之一,現今
Linux 上最熱門的程式開發工具。Gtk 原本是著名繪圖軟體 GIMP 的專屬開發工具,但後來 GIMP
的開發者將其獨立出來成為泛用的開發工具,因而改名為 Gtk[color="#993300"]+。現在 Gtk+ 已發展成物件導向的跨平台開發程式,目前最新的穩定版本為
2.12.0。
由於 Gtk+ 的授權方式為 LGPL,所以人們可以使用 Gtk+ 2.0 來開發 ISCL(比
GPL 更寬鬆)軟體或是商業軟體[color="#9999ff"](不釋出原始碼)而不必擔心會違反
LGPL 授權條款;而這也是 Gtk+2 會比 Qt 更受歡迎的主要原因之一:若使用 Qt 來開發軟體但不願購買 Qt 的使用授權,那麼該
Qt Based 軟體在釋出時將必須採用 GPL 授權。
Gtk+ 一向以輕巧穩定著名。許多以輕巧著稱的軟體,如:GCIN、GQView、Leafpad 都是 Gtk+2
based 軟體。一些極著名的專案,像是 GIMP、Pidgin、GNOME 也都是基於 Gtk+2。Linux 版的 Firefox
也採用了 Gtk+2 based 的介面。商業版的 Nero Linux 也是基於 Gtk+2 的。您可以發現,Gtk+2 的軟體幾乎無所不在。
Gtk+ 主要是以 C 語言寫成的,其主要元件如下:
[color="#3366ff"]GDK
[color="#9999ff"](GIMP Drawing Kit):封裝了 Xlib[color="#9999ff"](視窗低階函式)的函式庫。
[color="#3366ff"]Gdk-Pixbuf:
用來處理圖形的函式庫。
[color="#3366ff"]GLib:
它提供了一些替代函式來取代標準函式,用來增強 Gtk+ 的可移植性。
[color="#3366ff"]ATK [color="#9999ff"](Accessibility Tool Kit):讓程式能提供更友善、更易於操作的介面。
[color="#3366ff"]Pango:
用來處理國際化文字輸出。
Gtk+2 的開發介面主要是 C 語言。值得一提的是,Qt 的開發介面主要是 C++ 語言,或許這也是人們選擇 Gtk+2 或 Qt
的依據之一。但如果您習於使用 C++,但又想開發 Gtk+2 程式的話,可以替而使用
Gtkmm
這個 C++ 程式介面來開發 Gtk+ 程式碼。
範例程式
以下為本文做為範例的 Gtk+2 程式:
![]()
程式特點:
單一程式,不必藉助任何整合式開發工具。
啟動後,視窗會自動位於螢幕的中央。
依據 Gtk+2 的 rc 檔中的 gtk-alternative-button-order
決定按鈕的順序。
按下【檢視原始碼】按鈕,便會呼叫 leafpad 開啟 demo.c。
按下【取消】按鈕則會關閉本程式。
- 【取消】按鈕為 Gtk+2 制式按鈕。
無視 Window Manager 的關閉訊息。按下視窗上的【╳】按鈕時,程式不會關閉。
用滑鼠改變視窗大小時,仍能保持說明文字及按鈕的大小及相對位置。
- 支援 i18n。可輕易得將它翻譯成其它的語系。
完整原始碼 [color="#3366ff"]demo.c 如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
Gtk+2 必要的函式庫 [color="#336666"]*/
#include
[color="#336666"]/* 載入 gettext()。可讓程式支援 i18n。*/
#include
[color="#336666"]/* 指定一些在進行 [color="#336666"]L10N 時會用到的常數。
*/
/* 在此的 PACKAGE 是指定 mo 檔(翻譯檔)的名稱 */
#define PACKAGE "demo"
/* 而 LOCALEDIR 則指定了 mo
檔的位置。
*/
#define LOCALEDIR "./locale"
/* 在 gtkrc-2.0 中是否有設定
gtk-alternative-button-order?預設是 FALSE。 [color="#336666"]*/
gboolean [color="#666600"]BOTTON_ORDER=FALSE;
[color="#336666"]/* 事件
callback 函式。 */
gint delete( GtkWidget *widget,
GdkEvent
*event,
gpointer data )
{
[color="#336666"] /* 事件函式若回傳
TRUE[color="#336666"] 表示『事件己被本函
式處理完畢』。
*/
/* [color="#336666"]在本程式不做任何是卻只回應 TRUE 會讓本程式無法被 Window Manager 關閉。 */
/* 這類手法常見於『確定是否關閉本程式』這類訊問視窗上,
*/
/* 但在本程式裡,沒有實作訊問視窗。
*/
/* 但若回傳
FALSE,則
Gtk+2 會繼續處理並觸發 destory
事件來關閉程式。 [color="#336666"]*/
return [color="#666600"]TRUE;
}
[color="#336666"]/* [color="#336666"]訊號 callback 函式,[color="#336666"]呼叫 [color="#336666"]leafpad 開啟 demo.c。 [color="#336666"] */
void launch_leafpad( GtkWidget *widget,
gpointer data )
{
system("leafpad demo.c &");
}
int main( int argc,
char *argv[] )
{
/* GtkWidget 是在
Gtk 裡,用來宣告 Widget 元件的結構。 */
GtkWidget
*window; /* 宣告主視窗
[color="#336666"]*/
GtkWidget *vbox;
/* [color="#336666"]宣告垂直組裝盒
[color="#336666"]*/
GtkWidget
*label; [color="#336666"]/* [color="#336666"]宣告說明文字
[color="#336666"]*/
GtkWidget
*hbox; [color="#336666"]/* [color="#336666"]宣告水平組裝盒
[color="#336666"]*/
GtkWidget *launchbutton; /*
[color="#336666"]宣告呼
叫 leafpad 的按鈕 [color="#336666"]*/
GtkWidget *cancelbutton; /*
[color="#336666"]宣告關
閉按鈕
[color="#336666"]
*/
/* 實作
i18n,根據之前所定義的參數尋找 mo
檔[color="#336666"](翻譯檔)。 */
bindtextdomain ([color="#993300"]PACKAGE, [color="#993300"]LOCALEDIR);
/* 載入找到的 mo
檔[color="#336666"](翻譯檔)。
*/
textdomain ([color="#993300"]PACKAGE);
/* 和 X Server
連線並初始化 X 輸出輸入裝置。Gtk+2 Based 程式的必要步驟! */
/* 注意到,在使用任何 Gtk+2
的函式庫之前,必須先加以初始化。
*/
[color="#666600"]gtk_init (&argc,
&argv);
/* 檢查在 gtk 的 rc
檔中是否有設定 gtk-alternative-button-order? */
/* 將結果存放在 BOTTON_ORDER
變數裡。該變數已宣告於本程式最前頭。
*/
g_object_get
(gtk_settings_get_default (),
"gtk-alternative-button-order", &[color="#666600"]BOTTON_ORDER,
NULL);
/*
建立一個由 Window Manager
管理的視窗。
*/
[color="#993300"] /* [color="#993300"]小技巧:Gtk+2 元件都是直
接呼叫 gtk_[color="#666666"]widget_new() 來建立的。 */
/* 若在此使用 GTK_WINDOW_POPUP
則會建立一個無框架的視窗。 */
window = gtk_window_new ([color="#666600"]GTK_WINDOW_TOPLEVEL);
[color="#336666"]/* 指定視窗的標題。 [color="#336666"] [color="#336666"] */
gtk_window_set_title ([color="#666600"]GTK_WINDOW
(window), _("Gtk+2 demo by Tetralet"));
[color="#336666"]/* 指定視窗的位置為螢幕正中央。 [color="#336666"] [color="#336666"] */
gtk_window_set_position (GTK_WINDOW (window), [color="#666600"]GTK_WIN_POS_CENTER);
/* 指定視窗內部元件邊界為 15 pixel。
[color="#336666"]
*/
/* 注意到在此使用了 GTK_CONTAINER 巨集將
window 轉換為容器。 [color="#336666"] [color="#336666"] */
gtk_container_set_border_width ([color="#666600"]GTK_CONTAINER
(window), 15);
/*
按下視窗上的【關閉】按鈕時所產生的 delete_event
事件 [color="#336666"] [color="#336666"] */
[color="#336666"] /* 就交由 [color="#336666"]delete
函式處理。在本程式中,則只是單純得吃掉這個事件。 [color="#336666"] */
/* 注意到,我們傳給 delete_event
函式的自訂資料是空資料 (NULL)。 */
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK ([color="#666600"]delete),
NULL);
[color="#336666"]/* 建立一個(看不見)的 vbox[color="#336666"](垂直組裝盒)[color="#336666"],但不平均分配內部元件空間, [color="#336666"] [color="#336666"] */
[color="#336666"] /* 且[color="#336666"]內部元件間的間隔為 20 pixel。
[color="#336666"] [color="#336666"]*/
vbox = gtk_vbox_new (FALSE, 20);
/*
將這個 vbox 放到上面建立的 window
裡。
[color="#336666"] [color="#336666"] */
/* 注意到在此再次使用了 GTK_CONTAINER
巨集將 window 轉換為容器。 [color="#336666"] [color="#336666"] */
[color="#336666"]
gtk_container_add (GTK_CONTAINER (window), vbox);
/* 顯示這個 vbox。預設上 Widget 在建立之初都是看不見的, */
/* 所以建構完成後必須用 gtk_widget_show() 讓它顯現。
[color="#336666"]
*/
gtk_widget_show (vbox);
[color="#336666"]/* 建立一段文字說明。注意到,該文字是以 _("") [color="#336666"]包圍以利事後進行 L10N。 [color="#336666"] */
label = gtk_label_new ( [color="#993300"]_("Press
'View source code' button to open demo.c "
"by launching Leafpad;n"
"Press
'Cancel' button to exit.[color="#993300"]") );
[color="#336666"]/* 將
這個 label 放到上面建立的 vbox
上方。
[color="#336666"] */
[color="#336666"] /* [color="#336666"]第一個 FALSE:指定該元件的大小並不會隨著
vbox 大小而改變。 [color="#336666"]
*/
/* 第二個 FALSE
無作用(當上個參數設定為 TRUE 時才有作用)
[color="#336666"] */
[color="#336666"] /* 0:指定 [color="#336666"]其(上下)邊界為 0 pixel。
[color="#336666"] [color="#336666"]*/
gtk_box_pack_[color="#666600"]start
(GTK_BOX(vbox), label, FALSE, FALSE, 0);
/* 顯現這個
label。或許在內部建構完成再顯現會是比較好的做法。 [color="#336666"] [color="#336666"] */
gtk_widget_show (label);
/* 建立一個(看不見)的
hbox(水平組裝盒),並平均分配內部元件空間, [color="#336666"] [color="#336666"] */
[color="#336666"] /* 且[color="#336666"]內部元件間的間隔為 30 pixel。
[color="#336666"] [color="#336666"]*/
hbox = gtk_hbox_new (TRUE, 30);
[color="#336666"]/* 將
這個 hbox 放到上面建立的 vbox
下方。
[color="#336666"] [color="#336666"] */
[color="#336666"] /* [color="#336666"]第一個 FALSE:指定該元件的大小並不會隨著
vbox 大小而改變。 [color="#336666"]
*/
/*
如此一來,這個 hbox 將永遠會在視窗的最下方。 [color="#336666"] [color="#336666"] */
/* 第二個 FALSE
無作用(當上個參數設定為 TRUE 時才有作用)
[color="#336666"] */
[color="#336666"] /* 0:指定 [color="#336666"]其(左右)邊界為 0 pixel。
[color="#336666"] [color="#336666"]*/
gtk_box_pack_[color="#666600"]end
(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
[color="#336666"]/* [color="#336666"]顯示這個 hbox。 [color="#336666"] [color="#336666"] */
gtk_widget_show (hbox);
[color="#336666"]/* 建立第一個按鈕。 [color="#336666"] [color="#336666"] */
launchbutton = gtk_button_new_with_label (_("View
source code"));
[color="#993300"] /* 由 [color="#993300"]gtk-alternative-button-order 來決定按鈕的位置 */
if (BOTTON_ORDER)
[color="#336666"]/* 將這個 button 放到上面建立的 hbox
左方。
[color="#336666"] [color="#336666"] */
/* [color="#336666"]第一個 TRUE:指
定該元件的大小會隨著 vbox 大小而改變。 [color="#336666"] [color="#336666"] */
/* [color="#336666"]第二個 TRUE:若
有多餘的空間,則放在該 button 內部。 [color="#336666"] [color="#336666"] */
/* 0:指定 [color="#336666"]其(左右)邊界為 0 pixel。
[color="#336666"] [color="#336666"]*/
gtk_box_pack_start (GTK_BOX(hbox),
launchbutton, TRUE, TRUE, 0);
else
[color="#336666"]/* 將這個 button 放到上面建立的 hbox
右方。
[color="#336666"] [color="#336666"] */
gtk_box_pack_end
(GTK_BOX(hbox), launchbutton, TRUE, TRUE, 0);
[color="#336666"]/* [color="#336666"]點擊這個 button 所產生的 clicked
訊號就交由 launch_leafpad[color="#336666"]() 函式處理。 [color="#336666"] [color="#336666"] */
g_signal_connect (G_OBJECT (launchbutton), "clicked",
G_CALLBACK ([color="#666600"]launch_leafpad),
NULL);
[color="#336666"]/* [color="#336666"]顯示這個 button。 [color="#336666"] [color="#336666"] */
gtk_widget_show (launchbutton);
[color="#336666"]/* [color="#336666"]建立第二個按鈕。注意到我們這次建立了一個制式的 CANCEL 按鈕。 [color="#336666"] [color="#336666"] */
cancelbutton = [color="#666600"]gtk_button_new_from_stock
(GTK_STOCK_CANCEL);
/* 再一次的,由
gtk-alternative-button-order
來決定按鈕的位置[color="#336666"] */
if (BOTTON_ORDER)
[color="#336666"]/* 將這個 button 放到上面建立的 hbox
右方。
[color="#336666"] [color="#336666"] */
[color="#336666"] /* [color="#336666"]第一個 TRUE:指定該元件的大小會隨著 vbox
大小而改變。 [color="#336666"] [color="#336666"] */
[color="#336666"] /* [color="#336666"]第二個 TRUE:若有多餘的空間,則放在該
button [color="#336666"]內部。
[color="#336666"] [color="#336666"]*/
[color="#336666"] /* 0:指定
[color="#336666"]其(左右)邊界為 0 pixel。
[color="#336666"] [color="#336666"]*/
gtk_box_pack_end (GTK_BOX(hbox),
cancelbutton, TRUE, TRUE, 0);
else
[color="#336666"]/* 將這個 button 放到上面建立的 hbox
左方。
[color="#336666"] [color="#336666"] */
gtk_box_pack_start
(GTK_BOX(hbox), cancelbutton, TRUE, TRUE, 0);
[color="#336666"]/* [color="#336666"]點擊這個 button 所產生的 clicked
訊號就交由內建的 gtk_main_quit[color="#336666"]() 函式處理。 [color="#336666"] [color="#336666"] */
g_signal_connect (G_OBJECT (cancelbutton), "clicked",
G_CALLBACK
([color="#666600"]gtk_main_quit), NULL);
[color="#336666"]/* [color="#336666"]顯示這個 button。 [color="#336666"] [color="#336666"] */
gtk_widget_show (cancelbutton);
/* 顯示視窗。因為在本程式中,視窗是最上層的元件, */
/* 在它被顯現之前,它所包含的各個元件是看不見的。[color="#336666"] [color="#336666"] */
gtk_widget_show (window);
[color="#336666"]/* 開始監聽事件/訊號。注意到,必須呼叫 gtk_main_quit() 才能結束
gtk_main()。 */
gtk_main ();
[color="#336666"]/* 結束。[color="#336666"] */
return 0;
}
即使本程式有一半以上的文字是註解,但也才區區 156 行。因此,我們可以發現到,比起大部份的整合開發介面[color="#9999ff"] (IDE) 來,Gtk+2
的程式碼非常得簡潔明瞭,且只須使用文字編輯器就可以輕易得進行開發工作。這也是 Gtk+2 極為迷人的地方。
建立 Gtk+2 的 Widget 元件
在 Gtk+2 裡,像是視窗、選單、按鈕、文字標籤、輸入欄位... 等等視窗元件,都叫做 [color="#3366ff"]Widget。在 Gtk+2 裡,單一 Widget 的建構流程大略如下:
呼叫 gtk_[color="#006600"]widget_new()
建立該 Widget。
設定該 Widget 的屬性。像是指定它的標題文字或顏色之類的。
安排 Widget 在視窗中的位置。
將使用者動作和 callback 函式連結起來。如:按下某個按鈕之後,程式就會關閉。
最後,顯示元件。
以下我們會儘量按照這個步驟來介紹如何建構 Gtk+2 的元件。
安排 Widget 元件位置
圖形介面程式的最大優勢之一,就是在適切得組合種種的 Widget
元件後,使用者可以不必事先閱讀一大堆的使用手冊就能夠輕易得操作軟體。而該如何好好運用這項優勢可是在設計圖形介面程式時最該痛下苦功的地方。
一般的開發程式在配置視窗的版面時,往往會限定某個元件的大小、顏色、字型及位置,讓程式不管在任何環境下都能呈現相同的效果。但因為每個
人的作業環境往往大異其趣,這種作法實在並不是解決之道。
在 Gtk+2 裡則採用另一種觀點來解決這個問題。Gtk+2
使用『相對位置』來配罝元件,然後再根據使用者的作業環境及操作來動態決定元件的大小及位置及是否顯現。
為了讓開發者能靈活得配置 Widget 元件,Gtk+2 提供了一些『容器元件』。這些容器元件可以收納別的 Widget
元件,並藉此配置這些元件的位置:
Widget 容器:
有些 Widget 元件,如:視窗,在轉換成容器後,可以在其中放入另一個
Widget。像是可以把【按鈕】放入【視窗】裡,或是把一個【文字標籤】放入【按鈕】裡。以下範例是包含了一個『內含文字按鈕』的視窗:
![]()
vbox(垂直組裝盒,GtkBox 元件):
vbox 是一個看不見的元件,它能以『垂直』方式收納其它元件。以下範例是一個包含了四個按鈕的 vhox:
![]()
hbox(水平組裝盒,GtkBox 元件):
hbox 是一個看不見的元件,它能以『水平』方式收納其它元件。以下範例是一個包含了四個按鈕的 hbox:
![]()
搭配應用:
我們可以搭配使用 Widgit 容器、vbox、hbox 來設計出極複雜的介面。以本文的範例程式為例:
![]()
我們可以將這個視窗大略切割如下:
文字
按鈕
按鈕
也就是說,我們可以做如下的安排:
[color="#3366ff"]視窗
[color="#3366ff"]vbox
文字
[color="#3366ff"]hbox
按鈕
按鈕
在視窗裡放入 1 個 vbox,該 vbox 包含了 1 段文字及 1 個 hbox,該 hbox 則包含了 2 個按鈕:
[color="#ffffff"]○
[color="#ffffff"]Gtk+2 demo by Tetralet
[color="#ffffff"]ˍ□╳
按下【檢視原始碼】按鈕,便會呼
叫 leafpad 開啟 demo.c;
按下【取消】按鈕則會關閉本程式。
檢視原始
碼
取消
灰色部份:視窗
黃色部份:hbox
淺藍色部份:vbox
經由以上的簡短說明,或許您就能掌握到如何在 Gtk+2 裡配置 Widget 元件的訣竅了。
另外,Gtk+2 也支援使用『表格』來配置 Widget 元件的位置。請參考文未的參考資料以取得更進一步的資訊。
如何在程式碼裡安排 Widget 元件位置
在 Gtk+2 裡,要安排 Widget 元件位置其實很直覺:建立一個 GtkBox/Widget,然後把其它元件放入該
GtkBox/Widget 裡。
GtkBox 函式原型如下:
GtkWidget*
gtk_hbox_new (gboolean homogeneous,
gint spacing);
GtkWidget*
gtk_vbox_new (gboolean homogeneous,
gint spacing);
[color="#3366ff"]homogeneous
是指定是否要將在 GtkBox 裡的所有元件大小平均分配。
[color="#3366ff"]spacing
是指定在該 GtkBox 中的元件之間的空隙,單位是像素[color="#9999ff"] (pixel)。
然後,使用 gtk_box_pack_start() 或 gtk_box_pack_end() 將
Widget 元件加入 GtkBox 裡。其函式原型如下:
[color="#336666"]gtk_box_pack_start(GtkBox
*box,
GtkWidget *child,
gboolean expand,
gboolean fill,
guint padding);
[color="#336666"]gtk_box_pack_end(GtkBox
*box,
GtkWidget
*child,
gboolean expand,
gboolean fill,
guint padding);
[color="#3366ff"]box
指的是某個 vbox 或 hbox;
[color="#3366ff"]child
是要加入到該 vbox/hbox 裡的元件。
[color="#3366ff"]expand
是指定 child 所佔有的空間會不會隨著 box 大小的改變而改變。
若指定是 FALSE,
則 child 僅會佔用最小的空間。
[color="#3366ff"]fill
是指定當 box 的大小改變時,多餘的空間是放在 child 的內部,還是周圍。
若指定是 TRUE,則 child
的大小會直接隨著 box 大小的改變而改變。
若指定是 FALSE,則 child
的空間會隨著 box 大小的改變而改變,但本身大小不變。
fill 只有當 expand
為 TRUE 時方有作用。
[color="#3366ff"]padding
是指定 child 的上下[color="#9999ff"](左右)邊界,單位是像素。
而若是 Widget 元件,則可以呼叫 GTK_CONTAINER () 巨集將其轉換成容器後,利用 gtk_container_add()
將另一個 Widget 放入其中。其函式原型如下:
void gtk_container_add
(GtkContainer *container,
GtkWidget *widget);
請搭配範例程式裡的說明來進一步了解如何實作的一些細節。
Gtk+2 按鈕的順序
Gtk+2 有個極討人厭的地方,就是它的【OK】和【Cancel】這兩個按鈕位置和 Win32/Qt
等等大部份應用程式是相反的。請參考:
Linuxer
- 不會抱怨的一群人
平心而論,這麼做的確是蠻討人厭的。在 Gtk+1 裡原本是沒有這個問題的,但在 Gtk+2 裡卻做了如此的安排。個人認為,這是
Gtk+2 的開發者一項極不尊重使用者的決定。
其實,Gtk+ 的 rc 檔裡有個 gtk-alternative-button-order
設定可用來決定按鈕的順序。當它為 TRUE 時,它的順序是類似於 Win32/Qt/Gtk+1 那樣【OK】按鈕在前;FALSE
時它的順序則是類似於 Mac 那樣【Cancel】按鈕在前。有趣的是,在 Linux 版裡,這個設定預設是 FALSE 的,但在 Win32
裡,這個設定卻是 TRUE。
但大部份 Gtk+2 based 的 Linux/Win32 跨平台應用程式,像是 GIMP、Pidgin
等等,對此支援並不夠完善。即使設定了 gtk-alternative-button-order,有些畫面是【OK】在前,有些
畫面卻又是【OK】在後;且就算是按鈕的位置回復正常了,但整個版面配置反而讓人覺得怪怪的。也只能希望 Gtk+2 的開發者們能為此多多費心了。
我們在上面的範例程式中已有示範如何根據 gtk-alternative-button-order
來安排按鈕的位置。個人建議 Gtk+2 的開發者能儘量針對這個問題適當得配置程式的版面以符合使用者的個別需求。
程式開發
在開發 Gtk+2 程式時大多是採用 C,很多人認為這是 Gtk+2 如此穩定的主因之一。我們並不打算在此介紹如何以 C
開發程式,請到書局買本好書回來啃吧!
以下僅介紹一些在開發 Gtk+2 程式碼時的一些注意事項:
資料型態
為了可攜性及跨平台,Gtk+[color="#9999ff"](藉由 GLib)使用了一些自訂的資料型
態:
gint
gunit
gchar
guchar
glong
gulong
gfloat
gdouble
gshort
gint8
guint8
gint16
gunit16
gint32
guint32
gboolean
gpionter,類似於
(viod *)
物件系統
在開發 Gtk+2 程式時,其程式介面是 C。對於老一輩的程式開發員而言,只要將程式碼適切得模組化,他們較習於使用 C
這類『任務導向』的開發工具。但對於新一代的程式開發員而言,可能會寧願使用 C++ 這類能支援『物件導向』的開發工具。
但可喜的是,Gtk+2 利用了 GObject 函式庫來支援物件,讓 Gtk+2 裡的元件都具有物件導向的特徵,且利用 callback
函式讓在 Gtk+2 裡所有的 Widget 元件都能夠藉由事件驅動。
但牽扯到如何以物件導向的思維來設計程式就遠超過本文的範圍了。讓我們在此略過吧!
註解
在程式碼裡加入註解時,請使用 C 風格的 /* */,因為並不是所有的編譯器都能正確解讀 // 這種 C++
風格的註解。
GLib
GLib 是一個替 GDK 和 GTK
應用程式提供了包含很多常用的資料型態、函式及巨集的低階函式庫。其中包括了:
- 和作業平台無關的基本類型。像是 gint。
- List 操作函式
- 記憶體管理
- 字串處理
- 時間函式
- 除錯訊息
採用 GLib 可以確保所開發出來的程式能夠更容易移植至其它平台。另外的優點是採用這些函式可以有效避免像是『緩衝區溢位錯誤』之類的致命錯誤。
詳細資訊請參考:
GLib
Reference Manual
事件 (event)、信號 (signal) 和 回呼函式 (callback)
大部份的文字介面應用程式會根據使用者所指定的參數而執行不同的函式,而像是 Gtk+2
這類的圖形介面程式其實在本質上並沒有什麼不同 - 應用程式會啟用 event listeners(在
Gtk+2 裡就是 gtk_main ())等待著使用者的動作,並根據使用者的動作[color="#9999ff"](如:用滑鼠點擊按鈕)執行適當的回應(即:執行某個函
式),這些函式在 Gtk+2 中就叫做 [color="#993300"]callback
函式。而我們所欠缺的只是如何將這些 callback 函式和使用者的動作連結起來。
Gtk+2 將使用者的動作分成兩大類:
事件:
由應用程式外部所發生的事件,如使用者按下視窗上的【╳】關閉按鈕或是改變視窗的大小。一般這些事件是由 Window
Manager 所傳來的。其 callback 函式原型為:
gint callback_func(
GtkWidget *widget,
GdkEvent *event,
gpointer callback_data );
如果事件處理函式的傳回值為 TRUE,則表示該函式已將事件處理完畢。否則,會依 GTK
內建的處理流程來呼叫其它函式繼續處理事件,直到某個處理函式傳回值為 TRUE,或是執行到流程結束為止。
信號:
由應用程式內部所傳出的信號,如使用者按下視窗內的某個按鈕或是在視窗內部按下滑鼠右鍵等等。其 callback 函式原型為:
void callback_func(
GtkWidget *widget,
gpointer callback_data );
連結回呼函式:
在設計好 callback 函式後,我們必須將這些 callback 函式和使用者的動作連結起來。
其語法為:
gulong
g_signal_connect( gpointer *object,
const gchar *name,
GCallback func,
gpointer func_data );
其中的 [color="#3366ff"]object 指的是事件發生地的
Widget,但在此我們必須使用 G_OBJECT () 來將 GtkWidget 轉換成 object
型態。[color="#3366ff"]name 指的就是『事件名稱』像是 "clicked"。[color="#3366ff"]func 就是上文中所提及的 callback 函式。[color="#3366ff"]func_data 參數可以是任何資料,如:常數[color="#9999ff"](某個按鈕被按下)、GtkWidget(傳遞是哪個元
件,以便讀取其內容),或其它的任意資料。這些資料也就是傳遞給 callback_func
的 [color="#3366ff"]callback_data 欄位資料。
callback 函式的傳回值為其 id。可以利用其它函式來 切斷/暫時關閉/啟用 callback
函式的連結。例如,以下函式可以用來切斷剛才用 g_signal_connect 所連結的 callback 函式:
void
g_signal_handler_disconnect( gpointer object,
gulong id );
其中的 id 就是上文中 callback 函式的傳回值。
也可以同時連結至多個 callback 函式。這時 gtk_main () 將
會依照連結的先後,依序呼叫這些函式。
Gtk2+ 的 main() 的最後一定會呼叫 gtk_main () 函式。gtk_main
() 會等待事件/信號的發生、處理這些事件/信號並交由指定的 callback 函式處理。在關閉程式時,必須呼叫 gtk_main_quit
() 來結束 gtk_main () 函式,否則程式不會結束。
請參考上文中的範例程式中來了解 callback 函式是如何運作的。\n支援多國語系
為了讓程式能讓世界上更多的人們使用,讓程式能夠支援 i18n(internationalization,
國際化),也就是讓程式的介面能夠翻譯成各國語系,是最基本的要求了。
一般的做法是將程式裡所有的文字用 [color="#3366ff"]gettext()
抽出來,然後在程式執行時依實地需求而轉譯成別國的語言。像是把英文的[color="#3366ff"]【Apply】按鈕轉
譯成[color="#3366ff"]【套用】[color="#9999ff"](繁體中文)、[color="#3366ff"]【應用】[color="#9999ff"](簡體中文)或[color="#3366ff"]【適用】[color="#9999ff"](日文),這麼一來各國的人們就
能以親切的當地語言來使用這一套軟體了。這個翻譯過程就叫做 L10N (localization,區
域化)。
在 Gtk+2 裡,要讓程式支援 i18n 並非難事。首先,請先在程式開頭引入以下檔案,讓程式支援 gettext():
/*
載入 gettext()。可讓程式支援 [color="#336666"]i18n。*/
#include
然後,指定以下資訊:
/*
指定一些在進行 [color="#336666"]L10N 時會用到的常數。 */
/* 在此的 PACKAGE 是指定 mo 檔(翻譯檔)的名稱 */
#define [color="#993300"]PACKAGE
"demo"
/* 而 LOCALEDIR 則指定了 mo
檔的位置。
*/
#define [color="#993300"]LOCALEDIR "./locale"
並在 main() 中將 mo 載入:
/* 根據之前所定義的參數尋找 mo
檔[color="#336666"](翻譯檔)。 */
bindtextdomain ([color="#993300"]PACKAGE,
[color="#993300"]LOCALEDIR);
/* 載入找到的 mo
檔[color="#336666"](翻譯檔)。
*/
textdomain ([color="#993300"]PACKAGE);
這麼一來,程式在執行期間就會到 ./locale 下尋找 demo.mo 做為翻譯檔了。
接下來,在程式中就以 [color="#3366ff"]_("") 來標示有哪些文字將會被 gettext()
取代成翻譯文字。例:
/*
建立一個寫有 "View source code" 的按鈕。 [color="#336666"] [color="#336666"] */
launchbutton = gtk_button_new_with_label (_("View
source code"));
在程式完成後,我們要產生包含了那些翻譯字句的 pot 檔[color="#9999ff"](範本檔),讓翻譯者能夠據此進
行翻譯。其指令為:
xgettext --from-code=UTF-8 -k_ -o
demo.pot demo.c
其中,指定 --from-code 的原因是本程式裡有太多中文註解,若不指定的話 xgettext
會發生錯誤。-k_ 指定了我們是以 [color="#993300"]_("")
來表示這些文字是待翻譯文字。
在程式發佈之後,各語系的翻譯者就將上文中所產生的 demo.pot 重新命名為 po[color="#9999ff"] 檔[color="#9999ff"](翻譯檔)並進行翻譯。以
zh_TW.po 為例,其內容可能如下:
#
Translation of Demo.
# Copyright (C) 2007 Tetralet example.net>
# This file is distributed under the same license as the Demo package.
msgid ""
msgstr ""
"Project-Id-Version: Demon"
"Report-Msgid-Bugs-To: tetralet@example.netn"
"POT-Creation-Date: 2007-10-05 00:06+0800n"
"PO-Revision-Date: 2007-10-05 00:13+0800n"
"Last-Translator: Tetralet example.net>n"
"Language-Team: Tetralet example.net>n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=UTF-8n"
"Content-Transfer-Encoding: 8bitn"
#: demo.c:62
msgid "Gtk+2 demo by Tetralet"
msgstr "Gtk+2 示範程式 by Tetralet"
#: demo.c:84
msgid ""
"Press 'View source code' button to open demo.c by launching Leafpad;n"
"Press 'Cancel' button to exit."
msgstr ""
"按下【檢視原始碼】按鈕,便會呼叫 leafpad 開啟 demo.c;n"
"按下【取消】按鈕則會關閉本程式。"
[color="#336666"]#: demo.c:108
msgid "View source code"
msgstr "檢視原始碼"
翻譯者翻譯完成後,會將翻譯成果回報給程式開發者。開發者接著利用 msgfmt 將收到的 zh_TW.po
檔轉換成 demo.mo 檔,並放置到適當的位置:
msgfmt -o demo.mo zh_TW.po
mv demo.mo ./locale/zh_TW/LC_MESSAGES/
之後,程式在執行時就會依照使用者的 Locale 設定來載入 demo.mo 檔了。以 LANGUAGE=zh_TW.UTF-8
為例,本程式會依以下次序試圖載入 demo.mo 檔:
./locale/zh_TW.UTF-8/LC_MESSAGES/demo.mo
./locale/zh_TW.utf8/LC_MESSAGES/demo.mo
./locale/zh_TW/LC_MESSAGES/demo.mo
如果一切順利的話,該程式就會以親切的繁體中文顯示了。
為
了讓 Gtk+ Based 程式能夠為更多的人採用,除了 i18n 之外,Gtk+2 在多國語系共存方面也下了極大的苦心。使用 Gtk+2
來設計軟體將能確保程式能輕易得支援 m17n (multilingualization,多國語言
化)
而不必擔心會發生什麼缺字漏字、亂碼等情況發生。這也是個人認為 Qt 還有待加強的地方之一。
編譯
使用以下指令來編譯 Gtk 軟體:
gcc demo.c -o demo
`pkg-config --cflags --libs gtk+-2.0`
其中的 pkg-config 指令是用來決定 GTK 的編譯選項。pkg-config --cflags gtk+-2.0
會列出 include 目錄;pkg-config --libs gtk+-2.0 會列出編譯函式庫;兩者合併後就成了 pkg-config
--cflags --libs gtk+-2.0。
而 `` 則是一種 Shell 裡的置換語法。Shell 會先執行 ``
裡的指令,並轉換為字串放回原始指令中。
如果您開發的是一個大型的專案,您可能得動用到 make 來幫助您編譯軟體。請參考:
Makefile
語法簡介
程式除錯
即使沒有 IDE 這種整合式開發介面,要進行 Gtk+2 的除錯也並非難事。請參考:
Linux
除錯利器 - GDB 簡介
整合式開發介面
Gtk2+ 有個名為 glade 的專屬的整合式開發介面 [color="#9999ff"](IDE)。
若您不習慣使用文字介面來進行程式的開發,那麼 glade 將會是不錯的選擇。以下為其執行畫面:
![]()
其它程式範例
Gtk+2 還提供了一個 gtk-demo 程式(請安裝 gtk2.0-examples
套件),裡面提供了很多 Gtk+2 的元件範例及程式碼,極具參考價值。以下為其執行畫面:
![]()
參考資訊
在 Gtk 的官方網站上還提供了一份 Gtk+2
的入門教學。這份文件雖然已許久未更新,且有一部份文份尚待完成,但對於新手而言仍是極具參考價值。請參考:
GTK+ 2.0 Tutorial
另外,還有一份 GTK 的參考手冊,裡面詳盡記載了 GTK+ 所提供的函式原型。是在設計 GTK+2 時不可或缺的參考文件。請參考:
GTK+
Reference Manual
總結
以上只是極簡略得介紹 Gtk+2 的一些設計技巧,並只介紹了
window、button、label、hbox 及 vbox 等幾個較為簡單的 Widget。但若能就此掌握 Gtk+2
的開發流程,並對如何建構各種的 Widget 能更加熟悉的話,那麼成為一個稱職的 Gtk+2 程式設計師應該是指日可待的事了。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/28158/showart_1809359.html |
|