--------------------------------------------------------------------------------
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論壇】
|