在本章中,我们将讲述如何将构件布置在窗口与对话框中。
当我们在设计应用程序的图形界面时,我们首先要决定的是在程序中用到哪种构件和管理应用程序中的这些构件。为了方便管理我们的构件,在GTK+通常使用不可见的构件称作layout containers
. 。在本章节中,我们将设计其中的—— GtkAlignment
, GtkFixed
, GtkVBox
和 GtkTable
.
容器构件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
是一种用于垂直布局的容器型构件。他把放置在他中的子构件放置在一个单独的列中。类似的是 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
布局构件即可以按照行也可以按照列来布局她的子构件。
#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
容器构件控制了她的子构件的对齐方式与大小。
#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 )中只能放置一个子构件,这就是为什么我们要用到那么多盒子来帮助我们布局那两个按钮了。
接下来我们将展示一个更加高级一点的例子。具体就是展示一个窗口,你可以在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)中,这样我们就可以把他俩布局在顶部了。