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

首頁 > 編程技術 > 其它 > 正文
GTK入門導引--4. 封裝物件
翻譯: Brian Lin, OK STATION, Webmaster (2001-04-27 13:24:37)
   
--------------------------------------------------------------------------------

4. 封裝物件
當我們制作一套軟體, 您會希望在視窗內放超過一個以上的按鈕. 我們第一個范例"hello world"僅用一個物件, 因此我們能夠很簡單使用gtk_container_add來"封裝"該物件到視窗中. 但當您希夠望放更多的物件到視窗中, 要如何控制它們的位置? 這裡就要用到"封裝"(Packing). 

4.1 Packing Boxes的理論 
大部份的封裝是由產生boxes來達成的. 這些是看不見的widget containers, 我們可以用兩種形式來將我們的物件封裝進去, vertical box及horizontal box. 當我們封裝物件到一個horizontal box時, 物件是依我們呼叫的順序由右至左平行的被新增進去. 在vertical box, 物件是由上至下. 您可以將物件插入box, 也可以將boxes插入box, 任意的組合用以產生所想要的效果. 

要產生horizontal box,我們使用gtk_hbox_new(), 而vertical boxe使用gtk_vbox_new(). gtk_box_pack_start()及gtk_box_pack_end()函數是用來將物件放到containers裡面. gtk_box_pack_start()函數會開始由左至右, 由上至下來封裝物件. gtk_box_pack_end()則相反, 由下至上, 由右至左. 使用這些函數允許我們對右邊或對左邊較正, 而且可以用許多種方式來較正來取得所想要的效果. 一個object可以是另一個container或物件. 而且事實上, 許多物件本身也是containers. 像按鈕就是, 不過我們一般只在按鈕中用一個標簽. 

使用這些呼叫, GTK知道要把物件放到那裡去, 並且會自動縮放及其它比例上的調整. 還有許多其它選項可以控制如何將物件封裝在一起. 正如您所想的, 這些方法可以給您許多的彈性來制作視窗. 

4.2 Boxes詳述 
由於這樣的彈性, packing boxes在一開始使用的話會有點搞糊塗. 還有許多其它的選項,一開始還看不太出來它們如何湊在一起. 最後您會知道, 他們基本上有五種不同的型式. 


 


每一行包含一個horizontal box (hbox)及好幾個按鈕. 所有按鈕都是以同樣的方式來包入hbox內. 

這是gtk_box_pack_start的宣告. 


void gtk_box_pack_start (GtkBox    *box,
                         GtkWidget *child,
                         gint       expand,
                         gint       fill,
                         gint       padding);

第一個參數是您要把object放進去的box, 第二個是該object. 現在這些物件將會都是按鈕. 

expand參數在gtk_box_pack_start()或gtk_box_pack_end()中控制物件如何在box中排列. expand = TRUE的話它們會填滿box中所有額外的空間. expand = FALSE的話, 該box會縮小到剛好該物件的大小. 設expand=FALSE您可做好左右較正. 否則它們會填滿整個box. 同樣的效果可用tk_box_pack_start或pack_end functions來達成. 

fill參數在gtk_box_pack中控制額外空間. fill=TRUE該物件會自行產生額外空間, fill=FALSE則由box產生一個在物件周圍的填白區域. 這只有在expand=TRUE時, 才會有作用. 

當產生一個新的box, 該函數看起來像這樣: 


GtkWidget * gtk_hbox_new (gint homogeneous,
                          gint spacing);

homogeneous參數在gtk_hbox_new (and the same for gtk_vbox_new) 控制每個物件是否有同樣的寬或高. 若homogeneous=TRUE, 則expand也會被開啟. 

空白(spacing)及填白(padding)有什麼不同呢空白是加在物件之間, 填白只加在物件的一邊. 看以下這張圖可能會明白一點: 

 

這裡是一些用來產生以上影像的程式. 我做了蠻多的注解, 希望您不會有問題. 將它編譯然後玩玩它. 


4.3 封裝示范程式 


#include "gtk/gtk.h"

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

/* Make a new hbox filled with button-labels. Arguments for the 
 * variables we're interested are passed in to this function. 
 * We do not show the box, but do show everything inside. */
GtkWidget *make_box (gint homogeneous, gint spacing,
                     gint expand, gint fill, gint padding) 
{
    GtkWidget *box;
    GtkWidget *button;
    char padstr[80];
    
    /* create a new hbox with the appropriate homogeneous and spacing
     * settings */
    box = gtk_hbox_new (homogeneous, spacing);
    
    /* create a series of buttons with the appropriate settings */
    button = gtk_button_new_with_label ("gtk_box_pack");
    gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
    gtk_widget_show (button);
    
    button = gtk_button_new_with_label ("(box,");
    gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
    gtk_widget_show (button);
    
    button = gtk_button_new_with_label ("button,");
    gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
    gtk_widget_show (button);
    
    /* create a button with the label depending on the value of
     * expand. */
    if (expand == TRUE)
            button = gtk_button_new_with_label ("TRUE,");
    else
            button = gtk_button_new_with_label ("FALSE,");
    
    gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
    gtk_widget_show (button);
    
    /* This is the same as the button creation for "expand"
     * above, but uses the shorthand form. */
    button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
    gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
    gtk_widget_show (button);
    
    sprintf (padstr, "%d);", padding);
    
    button = gtk_button_new_with_label (padstr);
    gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
    gtk_widget_show (button);
    
    return box;
}

int
main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *box1;
    GtkWidget *box2;
    GtkWidget *separator;
    GtkWidget *label;
    GtkWidget *quitbox;
    int which;
    
    /* Our init, don't forget this! :) */
    gtk_init (&argc, &argv);
    
    if (argc != 2) {
        fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
        /* this just does cleanup in GTK, and exits with an exit status of 1. */
        gtk_exit (1);
    }
    
    which = atoi (argv[1]);

    /* Create our window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    /* You should always remember to connect the destroy signal to the
     * main window.  This is very important for proper intuitive
     * behavior */
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (destroy), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    /* We create a vertical box (vbox) to pack the horizontal boxes into.
     * This allows us to stack the horizontal boxes filled with buttons one
     * on top of the other in this vbox. */
    box1 = gtk_vbox_new (FALSE, 0);
    
    /* which example to show.  These correspond to the pictures above. */
    switch (which) {
    case 1:
        /* create a new label. */
        label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
        
        /* Align the label to the left side.  We'll discuss this function and 
         * others in the section on Widget Attributes. */
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

        /* Pack the label into the vertical box (vbox box1).  Remember that 
         * widgets added to a vbox will be packed one on top of the other in
         * order. */
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        
        /* show the label */
        gtk_widget_show (label);
        
        /* call our make box function - homogeneous = FALSE, spacing = 0,
         * expand = FALSE, fill = FALSE, padding = 0 */
        box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        /* call our make box function - homogeneous = FALSE, spacing = 0,
         * expand = FALSE, fill = FALSE, padding = 0 */
        box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        /* Args are: homogeneous, spacing, expand, fill, padding */
        box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        /* creates a separator, we'll learn more about these later, 
         * but they are quite simple. */
        separator = gtk_hseparator_new ();
        
        /* pack the separator into the vbox.  Remember each of these
         * widgets are being packed into a vbox, so they'll be stacked
         * vertically. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
        
        /* create another new label, and show it. */
        label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);
        
        /* Args are: homogeneous, spacing, expand, fill, padding */
        box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        /* Args are: homogeneous, spacing, expand, fill, padding */
        box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        /* another new separator. */
        separator = gtk_hseparator_new ();
        /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
        
        break;

    case 2:

        /* create a new label, remember box1 is a vbox as created 
         * near the beginning of main() */
        label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);
        
        /* Args are: homogeneous, spacing, expand, fill, padding */
        box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        /* Args are: homogeneous, spacing, expand, fill, padding */
        box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        separator = gtk_hseparator_new ();
        /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
        
        label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);
        
        /* Args are: homogeneous, spacing, expand, fill, padding */
        box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        /* Args are: homogeneous, spacing, expand, fill, padding */
        box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        separator = gtk_hseparator_new ();
        /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
        break;
    
    case 3:

    /* This demonstrates the ability to use gtk_box_pack_end() to
         * right justify widgets.  First, we create a new box as before. */
        box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
        /* create the label that will be put at the end. */
        label = gtk_label_new ("end");
        /* pack it using gtk_box_pack_end(), so it is put on the right side
         * of the hbox created in the make_box() call. */
        gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
        /* show the label. */
        gtk_widget_show (label);
        
        /* pack box2 into box1 (the vbox remember ? :) */
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        
        /* a separator for the bottom. */
        separator = gtk_hseparator_new ();
        /* this explicitly sets the separator to 400 pixels wide by 5 pixels
         * high.  This is so the hbox we created will also be 400 pixels wide,
         * and the "end" label will be separated from the other labels in the
         * hbox.  Otherwise, all the widgets in the hbox would be packed as
         * close together as possible. */
        gtk_widget_set_usize (separator, 400, 5);
        /* pack the separator into the vbox (box1) created near the start 
         * of main() */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);    
    }
    
    /* Create another new hbox.. remember we can use as many as we need! */
    quitbox = gtk_hbox_new (FALSE, 0);
    
    /* Our quit button. */
    button = gtk_button_new_with_label ("Quit");
    
    /* setup the signal to destroy the window.  Remember that this will send
     * the "destroy" signal to the window which will be caught by our signal
     * handler as defined above. */
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));
    /* pack the button into the quitbox.
     * The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
    gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
    /* pack the quitbox into the vbox (box1) */
    gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
    
    /* pack the vbox (box1) which now contains all our widgets, into the
     * main window. */
    gtk_container_add (GTK_CONTAINER (window), box1);
    
    /* and show everything left */
    gtk_widget_show (button);
    gtk_widget_show (quitbox);
    
    gtk_widget_show (box1);
    /* Showing the window last so everything pops up at once. */
    gtk_widget_show (window);
    
    /* And of course, our main function. */
    gtk_main ();

    /* control returns here when gtk_main_quit() is called, but not when 
     * gtk_exit is used. */
    
    return 0;
}



4.4 使用表格來封裝 
我們來看看另一個封裝的方法 - 用表格. 在很多狀況下, 這是極其有用的. 

使用表格, 我們產生格線來將物件放入. 物件會照我們安排的位置排入. 

我們第一個要看的是gtk_table_new這個函數: 


GtkWidget* gtk_table_new (gint rows,
                          gint columns,
                          gint homogeneous);

第一個參數是多少列, 第二個是多少欄. 

homogeneous參數用來決定表格如何來定大小. 若homogeneous為TRUE, table boxes會被重定為在其中最大物件的大小. 若homogeneous為FALSE, 則其大小為, "高"為列中最高的物件, 及"寬"欄中最寬的物件大小. 

列及欄的編號為從0到n. n是我們在gtk_table_new中所指定的值. 所以, 如果您指定rows = 2及columns = 2, 整個排列會看起來像這樣: 


 0          1          2
0+----------+----------+
 |          |          |
1+----------+----------+
 |          |          |
2+----------+----------+

坐標系統開始於左上角. 要把物件放進box中, 可用以下函數: 


void gtk_table_attach (GtkTable      *table,
                       GtkWidget     *child,
                       gint           left_attach,
                       gint           right_attach,
                       gint           top_attach,
                       gint           bottom_attach,
                       gint           xoptions,
                       gint           yoptions,
                       gint           xpadding,
                       gint           ypadding);

第一個參數("table")是您才剛產生的表格, 而第二個("child")是您想放進去的物件. 

而left_attach及right_attach參數指定要把物件放在那裡, 及用多少個boxes. 如果您想要用右下角的表格, 可以這樣填表. left_attach = 1, right_attach = 2, top_attach = 1, bottom_attach = 2. 

現在, 如果您想要物件來使用上面2x2的表格, 您可以使用left_attach = 0, right_attach =2, top_attach = 0, bottom_attach = 1. 

xoptions及yoptions是用來指定封裝選項, 可以同時組合多個選項(用or). 

這些選項是: 

GTK_FILL - 如果table box大過物件, 且GTK_FILL 被指定了, 該物件會擴展成使用所有可用的空間. 
GTK_SHRINK - 如果table widget小於該物件, (一般是使用者縮放該視窗), 那麼該物件將會一直被擠壓到看不見為止. 如果GTK_SHRINK被指定了, 該物件會跟table一起縮小. 
GTK_EXPAND - 這會使table本身擴展, 並利用視窗中所有可用空間. 
填空就像boxes, 產生一個在物件周邊空白的區域. 

gtk_table_attach()有許多選項. 這裡有個捷徑: 


void gtk_table_attach_defaults (GtkTable   *table,
                                GtkWidget  *widget,
                                gint        left_attach,
                                gint        right_attach,
                                gint        top_attach,
                                gint        bottom_attach);

X及Y選項內定為GTK_FILL | GTK_EXPAND, X及Y填空則設為0. 其餘的參數則相同於以上的函數. 

我們另外有gtk_table_set_row_spacing()及gtk_table_set_col_spacing(). 這些會在指定的欄及列插入空白. 


void gtk_table_set_row_spacing (GtkTable      *table,
                                gint           row,
                                gint           spacing);

及 
void       gtk_table_set_col_spacing  (GtkTable      *table,
                                       gint           column,
                                       gint           spacing);

對欄來說, 空格是在欄的右邊. 而列則是在下面. 

您也可以用以下函數來產生固定的空格. 


void gtk_table_set_row_spacings (GtkTable *table,
                                 gint      spacing);

及, 

void gtk_table_set_col_spacings (GtkTable  *table,
                                 gint       spacing);

使用這些函數, 其最後一欄及最後一列並沒有空格存在. 


4.5 Table Packing范例 
目前並無說明, 請參照testgtk.c 



--------------------------------------------------------------------------------
    (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)

===更多相關===
 

★  樊強制作 歡迎分享  ★