GTK+ 佈局管理
在本章中,我們將講述如何將構件佈置在窗口與對話框中。
當我們在設計應用程序的圖形界面時,我們首先要決定的是在程序中用到哪種構件和管理應用程序中的這些構件。爲了方便管理我們的構件,在GTK+通常使用不可見的構件稱作layout containers
. 。在本章節中,我們將設計其中的—— GtkAlignment
, GtkFixed
, GtkVBox
和 GtkTable
.
GtkFixed
容器構件GtkFixed
用於佈置子構件在一個固定的位置和設定固定的大小。這種構件並不是屬於自動的佈局關係器。實質上,在我們設計的大多數應用程序中,我並不使用GtkFixed;而在只用於一些比較特殊的場合。例如,遊戲,含有繪圖功能的專用軟件,那些需要移動和調整大小的軟件(正如電子表格中的圖表)以及那些小型的教育用途軟件。
#include <gtk/gtk.h> int main( int argc, char *argv[]) { GtkWidget *window; GtkWidget *fixed; GtkWidget *button1; GtkWidget *button2; GtkWidget *button3; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "GtkFixed"); gtk_window_set_default_size(GTK_WINDOW(window), 290, 200); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); fixed = gtk_fixed_new(); gtk_container_add(GTK_CONTAINER(window), fixed); button1 = gtk_button_new_with_label("Button"); gtk_fixed_put(GTK_FIXED(fixed), button1, 150, 50); gtk_widget_set_size_request(button1, 80, 35); button2 = gtk_button_new_with_label("Button"); gtk_fixed_put(GTK_FIXED(fixed), button2, 15, 15); gtk_widget_set_size_request(button2, 80, 35); button3 = gtk_button_new_with_label("Button"); gtk_fixed_put(GTK_FIXED(fixed), button3, 100, 100); gtk_widget_set_size_request(button3, 80, 35); g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; }
在我上面的這個例子中,我用代碼生成了三個按鈕構件然後把他們佈局在固定的座標上。當我們如果試圖去改變窗口的大小的時候,其中按鈕將會保持他們的大小和之前的座標。
fixed = gtk_fixed_new();
上面的代碼就可以生成了一個GtkFixed
的容器構件。
gtk_fixed_put(GTK_FIXED(fixed), button1, 150, 50);
第一個按鈕就 gtk_fixed_put()
函數來進行佈局,座標爲x=150, y=50.
GtkVBox
GtkVBox
是一種用於垂直佈局的容器型構件。他把放置在他中的子構件放置在一個單獨的列中。類似的是 GtkHBox
也有相似的功能,有區別的在於他是用於水平佈局,他的子構件是佈置在一個單獨的行中的。
#include <gtk/gtk.h> int main( int argc, char *argv[]) { GtkWidget *window; GtkWidget *vbox; GtkWidget *settings; GtkWidget *accounts; GtkWidget *loans; GtkWidget *cash; GtkWidget *debts; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 230, 250); gtk_window_set_title(GTK_WINDOW(window), "GtkVBox"); gtk_container_set_border_width(GTK_CONTAINER(window), 5); vbox = gtk_vbox_new(TRUE, 1); gtk_container_add(GTK_CONTAINER(window), vbox); settings = gtk_button_new_with_label("Settings"); accounts = gtk_button_new_with_label("Accounts"); loans = gtk_button_new_with_label("Loans"); cash = gtk_button_new_with_label("Cash"); debts = gtk_button_new_with_label("Debts"); gtk_box_pack_start(GTK_BOX(vbox), settings, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), accounts, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), loans, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), cash, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), debts, TRUE, TRUE, 0); g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window)); gtk_widget_show_all(window); gtk_main(); return 0; }
上面的這個按鈕就顯示了 GtkVBox
的作用。他把五個按鈕都佈局在同一列上。如果你改變程序窗口的大小,其中的子構件(如按鈕button)也會改變大小。
vbox = gtk_vbox_new(TRUE, 1);
上面程序中生成了GtkVBox
。 我們把其中的第一個參數設置爲 TRUE。這就意味着,程序中我的按鈕都爲同樣的大小。至於按鈕之間的距離大小被設置爲“1”象素。
gtk_box_pack_start(GTK_BOX(vbox), settings, TRUE, TRUE, 0);
在上面的程序中我們把“settings “按鈕佈局在vbox容器構件中。 至於函數實參中的前兩個參數,分別是容器構件和我們要放置的子構件。接下來的三個參數中分別是expand, fill和padding。 值得注意的是如果fill對應的參數是FALSE,則按鈕就不會充滿整個vbox構件。比較類似的是,如果之前在gtk_vbox_new(TRUE, 1);已經設置按鈕都是等寬高了,所以expand對應的參數,是完全沒有效果的。(譯者注:此處建議fill expand都設爲TRUE,至於具體區別和含義可在編程時感受)
GtkTable
GtkTable
佈局構件即可以按照行也可以按照列來佈局她的子構件。
#include <gtk/gtk.h> int main( int argc, char *argv[]) { GtkWidget *window; GtkWidget *table; GtkWidget *button; char *values[16] = { "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" }; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 250, 180); gtk_window_set_title(GTK_WINDOW(window), "GtkTable"); gtk_container_set_border_width(GTK_CONTAINER(window), 5); table = gtk_table_new(4, 4, TRUE); gtk_table_set_row_spacings(GTK_TABLE(table), 2); gtk_table_set_col_spacings(GTK_TABLE(table), 2); int i = 0; int j = 0; int pos = 0; for( i=0; i < 4; i++) { for( j=0; j < 4; j++) { button = gtk_button_new_with_label(values[pos]); gtk_table_attach_defaults(GTK_TABLE(table), button, j, j+1, i, i+1 ); pos++; } } gtk_container_add(GTK_CONTAINER(window), table); g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window)); gtk_widget_show_all(window); gtk_main(); return 0; }
在以上的例子中,我們將仿照計算器編寫一系列按鈕。
table = gtk_table_new(4, 4, TRUE);
上面我們生成了一個新的GtkTable
佈局構件,並設置爲4行與4列。
gtk_table_set_row_spacings(GTK_TABLE(table), 2); gtk_table_set_col_spacings(GTK_TABLE(table), 2);
上面我們就設置了每行與每列的距離。
for( i=0; i < 4; i++) { for( j=0; j < 4; j++) { button = gtk_button_new_with_label(values[pos]); gtk_table_attach_defaults(GTK_TABLE(table), button, j, j+1, i, i+1 ); pos++; } }
以上代碼將生成16個按鈕並把他們佈局在GtkTable容器構件中。
GtkAlignment
GtkAlignment
容器構件控制了她的子構件的對齊方式與大小。
#include <gtk/gtk.h> int main( int argc, char *argv[]) { GtkWidget *window; GtkWidget *ok; GtkWidget *close; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *halign; GtkWidget *valign; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 350, 200); gtk_window_set_title(GTK_WINDOW(window), "GtkAlignment"); gtk_container_set_border_width(GTK_CONTAINER(window), 10); vbox = gtk_vbox_new(FALSE, 5); valign = gtk_alignment_new(0, 1, 0, 0); gtk_container_add(GTK_CONTAINER(vbox), valign); gtk_container_add(GTK_CONTAINER(window), vbox); hbox = gtk_hbox_new(TRUE, 3); ok = gtk_button_new_with_label("OK"); gtk_widget_set_size_request(ok, 70, 30); gtk_container_add(GTK_CONTAINER(hbox), ok); close = gtk_button_new_with_label("Close"); gtk_container_add(GTK_CONTAINER(hbox), close); halign = gtk_alignment_new(1, 0, 0, 0); gtk_container_add(GTK_CONTAINER(halign), hbox); gtk_box_pack_start(GTK_BOX(vbox), halign, FALSE, FALSE, 0); g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window)); gtk_widget_show_all(window); gtk_main(); return 0; }
在上面的例子中,我們把兩個按鈕佈局在了一個窗口的右下角。爲了實現這個效果,我們用一個水平盒子構件horizontal box 、一個豎直盒子構件vertical box 和兩個對齊容器構件(alignment containers)。
valign = gtk_alignment_new(0, 1, 0, 0);
上面的代碼中我們生成了一個對齊容器構件。
gtk_container_add(GTK_CONTAINER(vbox), valign);
然後我們把對齊容器構件佈局在水平盒子中(vbox)。
hbox = gtk_hbox_new(TRUE, 3); ok = gtk_button_new_with_label("OK"); gtk_widget_set_size_request(ok, 70, 30); gtk_container_add(GTK_CONTAINER(hbox), ok); close = gtk_button_new_with_label("Close"); gtk_container_add(GTK_CONTAINER(hbox), close);
上面代碼中,我們生成了一個水平盒子( horizontal box) 然後把兩個按鈕佈局在其中。
halign = gtk_alignment_new(1, 0, 0, 0); gtk_container_add(GTK_CONTAINER(halign), hbox); gtk_box_pack_start(GTK_BOX(vbox), halign, FALSE, FALSE, 0);
上面的代碼中將生成一個對齊容器構件然後把佈局在她中的子構件佈局在右邊。我們把水平盒子( horizontal box)添加到對齊容器構件中,然後又把對齊容器構件添加到豎直盒子中(vertical box)。 最後,我要振臂高呼一下,:)對齊容器構件( alignment container )中只能放置一個子構件,這就是爲什麼我們要用到那麼多盒子來幫助我們佈局那兩個按鈕了。
Windows
接下來我們將展示一個更加高級一點的例子。具體就是展示一個窗口,你可以在JDeveloper IDE(一種java的集成開發軟件)中發現這個例子的身影。
The dialog shows all opened windows, or more precisely tabs in JDeveloper application.
#include <gtk/gtk.h> int main( int argc, char *argv[]) { GtkWidget *window; GtkWidget *table; GtkWidget *title; GtkWidget *activate; GtkWidget *halign; GtkWidget *halign2; GtkWidget *valign; GtkWidget *close; GtkWidget *wins; GtkWidget *help; GtkWidget *ok; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_widget_set_size_request (window, 300, 250); gtk_window_set_resizable(GTK_WINDOW(window), FALSE); gtk_window_set_title(GTK_WINDOW(window), "Windows"); gtk_container_set_border_width(GTK_CONTAINER(window), 15); table = gtk_table_new(8, 4, FALSE); gtk_table_set_col_spacings(GTK_TABLE(table), 3); title = gtk_label_new("Windows"); halign = gtk_alignment_new(0, 0, 0, 0); gtk_container_add(GTK_CONTAINER(halign), title); gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); wins = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE); gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1); activate = gtk_button_new_with_label("Activate"); gtk_widget_set_size_request(activate, 50, 30); gtk_table_attach(GTK_TABLE(table), activate, 3, 4, 1, 2, GTK_FILL, GTK_SHRINK, 1, 1); valign = gtk_alignment_new(0, 0, 0, 0); close = gtk_button_new_with_label("Close"); gtk_widget_set_size_request(close, 70, 30); gtk_container_add(GTK_CONTAINER(valign), close); gtk_table_set_row_spacing(GTK_TABLE(table), 1, 3); gtk_table_attach(GTK_TABLE(table), valign, 3, 4, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND, 1, 1); halign2 = gtk_alignment_new(0, 1, 0, 0); help = gtk_button_new_with_label("Help"); gtk_container_add(GTK_CONTAINER(halign2), help); gtk_widget_set_size_request(help, 70, 30); gtk_table_set_row_spacing(GTK_TABLE(table), 3, 6); gtk_table_attach(GTK_TABLE(table), halign2, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0); ok = gtk_button_new_with_label("OK"); gtk_widget_set_size_request(ok, 70, 30); gtk_table_attach(GTK_TABLE(table), ok, 3, 4, 4, 5, GTK_FILL, GTK_FILL, 0, 0); gtk_container_add(GTK_CONTAINER(window), table); g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window)); gtk_widget_show_all(window); gtk_main(); return 0; }
以上代碼將生成一個簡單的GTK+窗口。
table = gtk_table_new(8, 4, FALSE);
我們使用table表格容器構件來進行佈局。
title = gtk_label_new("Windows"); halign = gtk_alignment_new(0, 0, 0, 0); gtk_container_add(GTK_CONTAINER(halign), title); gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
上面的代碼生成了一個標籤,設爲居左。這個標籤被佈局在GtkTable
構件容器的第一列。
wins = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE); gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);
文本顯示構件佔據了兩行和兩列。我們把該文本編輯構件的屬性設置爲editable 和光標隱藏(hide the cursor)。
valign = gtk_alignment_new(0, 0, 0, 0); close = gtk_button_new_with_label("Close"); gtk_widget_set_size_request(close, 70, 30); gtk_container_add(GTK_CONTAINER(valign), close); gtk_table_set_row_spacing(GTK_TABLE(table), 1, 3); gtk_table_attach(GTK_TABLE(table), valign, 3, 4, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND, 1, 1);
我們把兩個靠在一起的按鈕佈局在文本編輯構件的左邊也就是第四行(我們是從0開始記數的),我們把這兩個按鈕佈局在“對齊構件”(alignment widget)中,這樣我們就可以把他倆佈局在頂部了。