[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 編程技術 > 其它 > 正文
GTK入門導引--2. 開始
翻譯: Brian Lin, OK STATION, Webmaster (2001-04-27 13:23:26)
   
--------------------------------------------------------------------------------

2. 開始
第一件要做的是當然是取得一份GTK的原始碼並且安裝進您的系統中. 您可以從GIMP取得一份發行版, 或者是從Peter Mattis's的"家中" ftp.xcf.berkely.edu/pub/pmattis(however, it has been changed to ftp.gimp.org)取得一份. GTK使用GNU的autoconf來設定. 一但您解開檔案, 輸入configure --help來看看選項表列. 

在介紹GTK的一開始, 我們盡可能挑最簡單的程式. 這個程式將會產生200x200點的視窗, 而且沒辦法離開, 除非從shell中將它殺掉. 


#include 

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, 其中宣告了所有變數, 函數, 及資料及結構. 這些東西您會在您的GTK應用軟體中用到. 

下一行 


gtk_init (&argc, &argv);

呼叫函數gtk_init(gint *argc, gchar ***argv)將會啟動GTK. 該函數設定了一些內定的值, 並且後續交給gdk_init(gint *argc, gchar ***argv) 繼續處理. 該函數啟動了一些函數庫以供使用, 設定了內定的信號處理, 檢查傳給您的程式的命令列參數. 看看以下: 


--display 
--debug-level 
--no-xshm 
--sync 
--show-events 
--no-show-events 
這些參數將會從參數表中刪去, 所剩下的會傳給您做後續的處理. 這樣會產生標準的參數表(除了GTK所使用的)以供您使用. 

下面這兩行程式會產生並顯示一個視窗. 


  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_show (window);

GTK_WINDOW_TOPLEVEL參數指定了我們承習視窗管理程式的外觀. 即便我們產生一個0x0大小的視窗, 沒有子視窗的視窗內定被設為200x200, 如此我們依然可以處理它. 

gtk_widget_show()函數, 讓GTK知道, 我們已經處理完設定其屬性的工作, 並且可以顯示它. 

最後一行進入GTK的主要處理回圈. 


gtk_main ();

gtk_main()是個在每個GTK應用軟體中都會看到的一個函數. 當控制到達這裡, GTK會"睡"一下來等待X事件的發生(諸如像按鍵被按下). 在我們最簡單的例子裡面, 事件會被忽略掉. 因為我們沒有處理它. 



2.1 用GTK來寫Hello World 
好, 現在我們來寫一個有一個視窗物件的視窗(一個按鈕). 這是個GTK的標準hello world. 這會建立起一個新的GTK軟體的良好基礎. 



#include 

/* 這是個callback函數. 其資料參數在本例中被忽略
 * 以下有更多的callback函數. */
void hello (GtkWidget *widget, gpointer *data)
{
    g_print ("Hello World\n");
}

/* another callback */
void destroy (GtkWidget *widget, gpointer *data)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
    /* GtkWidget用以儲存視窗物件形態 */
    GtkWidget *window;
    GtkWidget *button;
    
    /* 這在所有GTK應用軟體中用到. 參數由命令列中解譯出來並且送到該應用軟體中. */

    gtk_init (&argc, &argv);
    
    /* 產生新視窗 */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* 當視窗收到"destroy"信號時(可由該軟體或視窗管理程式所送出)
       所會被呼叫到的destroy函數一如以下所定義的一般.
       送到該函數的資料將會是NULL,並且在該函數中被忽略 */

    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (destroy), NULL);
    
    /* 設定視窗的邊框的寬度 */
    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    /* 產生一個新的按鈕並帶有"Hello World"的字在上面. */
    button = gtk_button_new_with_label ("Hello World");
    
    /* 當該按鍵收到"clicked"信號, 它會呼叫hello()這個函數.
       並且以NULL做為其參數. hello()函數在以上已定義過. */

    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (hello), NULL);
    
    /* 這會導致當"clicked"這個按鈕被按下的時候,
       呼叫gtk_widget_destroy(window)而使該視窗被關閉
       當然了, 關閉的信號會從此處或視窗管理程式所送來 */
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));
    
    /* 這個動作會把這個按鈕結合到該視窗(a gtk container). */
    gtk_container_add (GTK_CONTAINER (window), button);
    
    /* 最後一步是顯示最新產生的視窗物件... */
    gtk_widget_show (button);
    
    /* 及該視窗 */
    gtk_widget_show (window);
    
    /* 所有GTK程式都一定要有gtk_main(). 所有控制結束於此並等帶事件的發生
       (像按下一鍵或滑鼠的移動). */
    gtk_main ();
    
    return 0;
}


2.2 編譯Hello World 
用以下命令來編譯: 


gcc -Wall -g helloworld.c -o hello_world -L/usr/X11R6/lib \
    -lglib -lgdk -lgtk -lX11 -lXext -lm

函數庫必須在內定的搜尋路徑內, 如果找不到, -L 則gcc會去找這些目錄, 看看所需要的函數庫是否找得到. 例如, 在我的Debian Linux系統中, 我已經增加了 -L/usr/X11R6/lib用來尋找X11函數庫. 

以下函數庫是很重要的. linker在處理之前, 必須知道什麼函數要用那一個函數庫. 

函數庫如下: 

glib函數庫(-lglib), 包含一些有用的函數, 這個例子中只用到g_print(), 因為GTK是建在glib之上, 所以您幾乎都一定會用到它. 詳見glib一段. 
GDK函數庫(-lgdk), Xlib的包裝程式. 
GTK函數庫(-lgtk), 視窗物件函數庫, 基於GDK之上. 
xlib函數庫(-lXlib) 基本上為GDK所用. 
Xext函數庫(-lXext). 包含了shared memory pixmaps及其它的一些X extensions. 
math函數庫(-lm). 為GTK所用, 有多方面用途. 

2.3 Signals及Callbacks的原理 
在我們更進一步探討hello world之前, 我們要講一下事件(events)及回呼函數(callbacks). GTK本身是個事件驅動的工具, 這意味它會在gtk_main進入停歇狀態, 一直到一個事件發生, 並且將控制交給適當的函數來處理. 

控制權的交出是由"signals"來決定的. 當事件發生, 諸如按下滑鼠的一個按鍵, 對應的信號會由該視窗物件所送出. 這便是GTK的主要工作. 要使一個按下的動作執行一個命令, 我們設定一個信號處理函數來擷取這個信號, 並且呼叫適當的函數. 這工作是由像以下的函數來完成的: 


gint gtk_signal_connect (GtkObject *object,
                         gchar *name,
                         GtkSignalFunc func,
                         gpointer func_data);

其第一個參數是會送出信號的物件, 第二個是希望接取的信號名稱. 第三個是當信號送出時的接取函數, 第四個則是要送給該函數的資料. 

第三個參數被稱為"callback function", 而且必需是以下的形式: 


void callback_func(GtkWidget *widget, gpointer *callback_data);

第一個參數是指向該物件的指標, 第二個是在gtk_signal_connect()的最後一個參數. 

另外一個在hello world中有用到的函數是: 


gint gtk_signal_connect_object (GtkObject *object,
                                gchar  *name,
                                GtkSignalFunc func,
                                GtkObject *slot_object);

gtk_signal_connect_object()跟gtk_signal_connect()一樣, 除了callback函術只有一個參數, 一個指向GTK物件的指標. 所以當使用這個函數來接到信號時, 該callback函數必須是以下形式: 


    void callback_func (GtkObject *object);

一般這個object是個widget(物件). 我們一般不設定callback給gtk_signal_connect_object. 他們是用來呼叫GTK函數來接受單一物件(widget or object)做為參數. 

有兩個函數來連接信號的目的只是希望允許callbacks可以有不同數量的參數. 許多GTK函數僅接受一個GtkWidget指標做為參數, 所以您可以使用gtk_signal_connect_object()來使用這些函數, 而在您的函數裡面, 您會需要額外的資料提供給callback. 


2.4 步過Hello World 
現在您知道這些理論了, 我們現在來根據這些理論, 把"hello world"這個范例弄清楚. 

這是個當按鈕被按下時, 會被呼叫到的callback函數. 參數的資料沒有被用到. 


void hello (GtkWidget *widget, gpointer *data)
{
    g_print ("Hello World\n");
}

這是另一個callback函數, 它會呼叫gtk_main_quit()來離開程式. 

void destroy (GtkWidget *widget, gpointer *data)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{

下個部份, 宣告一個指標給GtkWidget. 這是準備用來產生視窗及按鈕的. 

    GtkWidget *window;
    GtkWidget *button;

這裡是我們的gtk_init. 設定GTK toolkit初始值. 

    gtk_init (&argc, &argv);

產生新視窗. 這是蠻直接的. 記憶體配置給GtkWidget * window使其成為有效的資料. 它設定一個新的視窗, 但在我們呼叫gtk_widget_show(window)之前不會顯示. 

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

這裡是將object(window)連接到信號處理器的范例. 此處"destroy"是該信號. 該信號是window manager要銷去這個視窗時, 或我們送出gtk_widget_destroy()時會產生的. 當我們這樣設定時, 我們可同時處理兩種狀況. 這裡我們使用destroy函數, 這使我們可以使用window manager來離開這個程式. 

GTK_OBJECT及GTK_SIGNAL_FUNC是分派巨集. 

    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (destroy), NULL);

下一個函數是用來設定container物件的屬性. This just sets the window so it has a blank area along the inside of it 10 pixels wide where no widgets will go. There are other similar functions which we will look at in the section on Setting Widget Attributes

And again, GTK_CONTAINER is a macro to perform type casting. 

    gtk_container_border_width (GTK_CONTAINER (window), 10);

這個會產生一個新的按鈕. 它配置記憶體給一個新的GtkWidget, 並初始化. 他將會有一個標簽"Hello World". 

    button = gtk_button_new_with_label ("Hello World");

然後, 我們讓這個按鈕做一點事. 我們將他接到一個信號處理器, 因此它會送出"clicked"信號, 而我們的hello()函數會被呼叫到. 資料被忽略, 所以我們只喂NULL給hello(), 明顯的, "clicked"信號當我們敲下滑鼠時被送出. 


    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (hello), NULL);

我們將用這個按鈕來離開程式. 這將展示"destroy"信號可以是來自window manager, 或是我們的程式. 當按鈕被"clicked", 跟上面一樣, 它會呼叫hello() callback函數, 然後是這一個, 以它們被設定的先後順序被呼叫到. 您可以有任意個callback函數, 它們會以被連接的先後順序被執行到. 因為gtk_widget_destroy()函數僅接受 GtkWidget *widget做為參數, 我們使用gtk_signal_connect_object() , 而不用gtk_signal_connect(). 


    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));

這是個封裝呼叫, 我們在後面的文件中會解釋. 不過這倒蠻容易理解的. 它就是告訴GTK按鈕要放在要顯示出來的那個視窗. 

    gtk_container_add (GTK_CONTAINER (window), button);

現在我們將所有東西照我們的意思來設定好了. 所有信號接好了, 按鈕也放到該有的位置, 現在來"show"這個視窗吧. 這個整個視窗會一下子從螢幕蹦出來, 而不是先看到視窗, 然後按鈕才跑出來. 

    gtk_widget_show (button);

    gtk_widget_show (window);

還有當然了, 我們呼叫gtk_main()來等待X事件的發生, 當事件發生時, 它將會呼叫物件來送出信號. 

    gtk_main ();

最後, 程式終止於此. 在gtk_quit()被呼叫到後, 程式會離開. 
    return 0;

現在, 當我們在GTK上敲下滑鼠, 這個物件會送出"clicked"信號. 我們的程式設定了信號處理器來接取這個信號, 這樣我們便可利用這個資訊. 在我們的范例中, 當按鈕被"clicked", hello()函數被呼叫到, 並被傳入一個NULL參數, 然後下一個處理函數被呼叫到. 它會呼叫gtk_widget_destroy()函數, 傳入視窗物件做為參數, 並將該視窗物件銷毀. 這會導致該視窗送出"destroy"信號, 收到該信號後, 會呼叫我們的destroy() callback函數, 而我們的destroy()會令程式離開GTK. 

另一個方式當然是利用window manager來銷毀該視窗. 這也會導致該視窗送出"destroy"信號, 然後呼叫destroy() callback, 然後離開. 

這些信號與UNIX系統不太一樣, 並非基於UNIX系統的信號系統, 雖然它們的術語是一樣的. 




--------------------------------------------------------------------------------
    (http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
GTK入門導引--結束語 (2001-04-27 13:38:49)
GTK入門導引--20. 寫出屬於您自己的物件 (2001-04-27 13:35:04)
GTK入門導引--19. GTK的rc檔 (2001-04-27 13:34:33)
GTK入門導引--18. 設定視窗物件屬性 (2001-04-27 13:34:13)
GTK入門導引--17. glib (2001-04-27 13:33:43)
GTK入門導引--16. 選取區域管理 (2001-04-27 13:33:18)
GTK入門導引--14. Menu物件 (2001-04-27 13:32:52)
GTK入門導引--13. Undocumented Widgets (2001-04-27 13:32:29)
GTK入門導引--12. List物件 (2001-04-27 13:31:41)
GTK入門導引--11. 檔案選取物件 (2001-04-27 13:31:14)

===更多相關===
 

★  樊強制作 歡迎分享  ★