GTK+學習:概述
相對於Swing之於Java,C/C++環境下的GUI構建就顯得複雜得多。首先就是C/C++語言並沒有一個官方的GUI庫。於是,第三方類庫就如雨後春筍般成長起來。由於C/C++沒有Java類似的跨平台性,所以其類庫大多也是限定平台的,比如微軟的MFC。當然也會有很多能夠跨平台的 GUI庫,比如Qt,比如wxWidget,比如我們這裡所要講解的GTK+。
相對於MFC、Qt和wxWidget,GTK+使用物件導向架構的純C語言編寫。這是一個小巧但是功能絲毫沒有遜色的GUI類庫。Linux 下的GNOME環境就是使用GTK+編寫的,還有類似於Photoshop的GIMP,更與GTK+有著密不可分的聯絡。下面先來簡單介紹一下GTK+。
說起GTK+,首先要先說GIMP。在很多推薦使用自由軟體代替商業軟體的文章中,大多會提到,使用開源的GIMP代替Photoshop。GIMP是GNU Image Manipulation Program的縮寫,最初是類Unix作業系統上的影像處理程式,現在也被移植到了Windows平台。為了簡化GIMP的開發,GIMP ToolKit誕生了,這就是GTK。在增加了物件導向的特性之後,它的名字後面添加了一個加號,於是就成為GTK+。
GTK+是一個建立圖形化使用者介面的庫,能夠運行於類Unix平台、Windows平台和其他裝置上。GTK+依賴於下面所列出的幾個庫:
* GLib - 一個通用的工具庫,並不僅僅用於建立圖形化使用者介面。GLib定義了很多資料類型、宏、檔案工具等;
* Pango - 國際化文字綁定;
* ATK - 提供圖形化使用者介面互動訪問技術的通用介面;
* GdkPixbuf - 允許從映像資料或者影像檔建立像素緩衝;
* GDK - 為不同的視窗系統提供的抽象層,提供本地圖形介面和GTK+間的一個抽象,是平台相關的。如果需要向其他平台上移植GTK+,只需要重新編寫GDK;
* GTK - GTK+庫,提供了各種控制項。
我們對GTK+的學習僅僅為了使用GTK+,不會過多的糾纏這些庫的使用。
GTK+學習:搭建環境
瞭解過GTK+之後,下面要進行的是環境搭建工作,以便進行GTK+的學習。環境的搭建需要分為Windows和Linux兩個平台:
Windows平台:
在[url]http://www.gtk.org/download.html[/url]可以找到Windows(32-bit)和64-bit的版本下載,按照自己的系統是32位還是64位選擇相應的版本,頁面中的GTK+ individual packages是GTK+運行所必須的庫,Third Party Dependencies是第三方依賴庫。這樣我們就能夠根據需要下載相應的庫檔案,能夠使GTK+庫最小化。如果想要簡單的話,頁面上也提供了一個 bundle包,這是將所有庫打包在一起的,可以下載這個使用。注意,bundle包並沒有單列出來,而是在頁面本文中給出的連結,可能需要小心找找。
這裡我們下載bundle包,解壓縮之後,可以將裡面的bin檔案夾添加到環境變數path裡面,以便我們編譯出的exe檔案運行時可以找到那些dll。否則的話需要將這些dll複製到和exe同一目錄下。還可以將bin裡面的這些dll全部複製到C:/WINDOWS/system32檔案夾下,因為即使將這些添加到系統內容變數,在IDE裡面運行也是找不到dll的,複製到system32檔案夾下就可以了。
下面使用VC6來配置編譯環境。
首先在VC6的Tools-Options下面的Directories選擇Include files,添加GTK+目錄下的include檔案夾以及裡面的所有一級子目錄,然後添加lib檔案夾下的子目錄中的include檔案夾,這樣的話一共是添加10個目錄:
然後在Library files裡面添加GTK+目錄下的lib檔案夾:
這樣之後VC6就已經配置好了,VS2008之類的也類似配置。下面建立一個控制台工程(VC6)或者是空工程(VS2008),開啟工程的 settings,在link選項卡下清空裡面原有的Object/library modules,然後添加glib-2.0.lib gtk-win32-2.0.lib gdk-win32-2.0.lib gobject-2.0.lib gdk_pixbuf-2.0.lib gthread-2.0.lib gmodule-2.0.lib pango-1.0.lib intl.lib 幾個,確定即可。這裡使用的是VC6,VS2008裡面也有類似的設定。
這樣設定之後可以建立一個main.c檔案,然後輸入:
#include <gtk/gtk.h>
int main(int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
編譯、連結、運行,有視窗出現,說明環境配置成功:
Linux平台:
首先聲明,我是用的Linux版本是Ubuntu,案頭環境是gnome。前面也說過gnome就是使用GTK+編寫,所以運行環境不需要另外配置了,要做的是安裝開發所需要的環境。
剛剛安裝好的Ubuntu已經預設安裝了gcc,但是並沒有安裝所需要的標頭檔,我們需要手動添加:
sudo apt-get install build-essential
然後再安裝gnome開發包:
sudo apt-get install gnome-core-devel
系統會自動找到所依賴的庫,並一起下載下來。
這樣安裝過後使用下面的命令編譯上面的代碼:
gcc main.c -o main `pkg-config --cflags --libs gtk+-2.0
注意,這裡的`是鍵盤上1左面的反引號,不是單引號。
這裡不清楚KDE案頭怎樣配置GTK+,可能步驟也類似,但是需要先安裝GTK+的運行環境吧。
上面是Windows和Linux平台下GTK+的環境配置。這些步驟在我的機器上已經測試通過了。在配置好環境後,下面我們就開始新的學習了。
GTK+學習:不僅僅是Hello world
一般在學習一項新技術的時候,我最希望看到的不是那一本本厚厚的理論書籍或者編程技巧,即便沒有什麼技巧,我希望的是能夠看到我的成果,即便只是一個小小的什麼都不能做的視窗,也能滿足一下我的好奇心。所以,現在我們先來寫一個小程式,看看GTK+程式是怎樣編寫的。
由於上次我們已經配置好了編譯環境,所以,直接開啟你所喜愛的IDE或者記事本,敲下如下的代碼。這裡我是在Windows平台下使用VS2005編譯的,有些術語在其他平台下可能會有出入,但整體是類似的。
#include <gtk/gtk.h>
int main(int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);
gtk_main();
return 0;
}
然後進行編譯串連,運行之後,如果沒有錯誤的話,將會出現一個小視窗。嗯,這就是我們用GTK+編寫的介面了。
下面來看看這段程式。首先第一行,是引用的標頭檔,一般來說只要引用這個gtk.h就可以了。按照前面所述的方法引入include檔案的話,就是在gtk目錄下的gtk.h了。
然後是main函數定義,和C語言一模一樣。
main函數中,第一句聲明一個GtkWidget的指標。前面說過,GTK+是按照物件導向思想設計的,不妨把這個GtkWidget當作類吧,雖然C編譯器並不這麼認為。然後是gtk_init調用,這是初始化GTK+環境。寫過OpenGL程式的朋友應該比較清楚,OpenGL裡面也有一個類似的init函數。然後對前面聲明的指標賦值。看看這個函數名字:gtk_window_new,很清楚是建立一個window指標。按照物件導向的寫法就是:
GtkWidget* window = new GtkWindow(GTK_WINDOW_TOPLEVEL);
怎麼樣。清楚很多了吧。其實這裡就已經暗示出了,GtkWindow繼承了GtkWidget類。這些在後面就會看出,GtkWidget其實是所有控制項的父類。傳遞的參數是GTK_WINDOW_TOPLEVEL,指明是一個頂層的視窗。然後使用gtk_widget_show設定顯示,同樣把它想象成物件導向的文法就是:
window -> show();
最後一句gtk_main,將我們的程式帶入GTK+的事件監聽迴圈。
這樣,我們的程式就介紹完了。但是也許就會發現一個問題:怎麼後面還有一個黑黑的控制台視窗啊。這是因為預設運行方式是Debug,換成Release看看。那個醜醜的視窗沒有了吧。而且產生的二進位檔案也比那個小了很多。
嗯,當我們按下關閉按鈕時,視窗是不見了,但是程式並沒有退出啊。對哦,因為我們沒有添加事件監聽啊,所以當關閉視窗的時候,GTK+也不知道該怎麼做。所以,我們繼續修改代碼如下:
#include <gtk/gtk.h>
int main(int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
注意,我們添加了一個事件監聽函數:
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
它的意思是,對於對象window,當"destroy"時間發生時,調用gtk_main_quit函數,傳給這個函數的參數是NULL。如果我們使用Java語言的事件監聽寫法,就是這樣子的:
window.addDestroyListener(new CallbackFunc(){
gtk_main_quit();
});
不過這段代碼應該也是比較容易讀懂的。其實這就是GTK+中添加事件監聽的寫法,每個控制項的每個事件監聽都是這樣編寫代碼,很統一。修改完成後運行一下,點下關閉按鈕:哈,程式退出了。
那麼,我們再來修改一下程式吧:
#include <gtk/gtk.h>
int main(int argc, char** argv)
{
GtkWidget* window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Hello world!");
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
運行一下看看。視窗標題已經換成了經典的Hello world!了。
讀一下代碼,我們添加了這麼一句:
gtk_window_set_title(GTK_WINDOW(window), "Hello world!");
如果你能想到它的物件導向文法,就達到目的了啊:
((GtkWindow*)window) -> setTitle("Hello world!");
注意,因為C編譯器是不理解物件導向的多態機制的,所以,GTK+使用了很多宏來進行類型轉換,比如這裡的GTK_WINDOW。還記得我們聲明的是GtkWidget指標,需要轉換成GtkWindow指標才能使用set_title函數的。同時,這個宏也有類型檢測的功能,如果不能轉換,是會拋出異常的。
好了,現在經過一步步的代碼添加,我們已經瞭解了GTK+程式的編寫過程,並且能夠建立一個表單,添加事件監聽和修改控制項屬性——GUI編程的主體不就是這些嗎。剩下的就是一些細節,和對於龐大的控制項陳列庫的學習了。