GTK+ 布局管理
最后修改于 2023 年 10 月 18 日
在本章中,我们将展示如何在窗口或对话框中布局我们的控件。
当我们设计应用程序的 UI 时,我们决定使用哪些控件以及如何组织这些控件。为了组织我们的控件,我们使用称为布局容器的特殊不可见控件。在本章中,我们提到了 GtkAlignment
、GtkFixed
、GtkVBox
和 GtkTable
。
GtkFixed
GtkFixed
容器将子控件放置在固定位置并具有固定大小。此容器不执行自动布局管理。因此,它不适用于翻译、字体更改或主题。在大多数应用程序中,我们不使用 GtkFixed
容器。可能有一些可以使用该容器的特殊区域(例如,定位图表或图像)。
#include <gtk/gtk.h> int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *fixed; GtkWidget *btn1; GtkWidget *btn2; GtkWidget *btn3; 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), 300, 200); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); fixed = gtk_fixed_new(); gtk_container_add(GTK_CONTAINER(window), fixed); btn1 = gtk_button_new_with_label("Button"); gtk_fixed_put(GTK_FIXED(fixed), btn1, 150, 50); gtk_widget_set_size_request(btn1, 80, 30); btn2 = gtk_button_new_with_label("Button"); gtk_fixed_put(GTK_FIXED(fixed), btn2, 15, 15); gtk_widget_set_size_request(btn2, 80, 30); btn3 = gtk_button_new_with_label("Button"); gtk_fixed_put(GTK_FIXED(fixed), btn3, 100, 100); gtk_widget_set_size_request(btn3, 80, 30); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; }
在我们的示例中,我们创建了三个按钮并将它们放置在固定坐标处。当我们调整应用程序窗口的大小时,按钮会保持其大小和位置。
fixed = gtk_fixed_new();
get_fixed_new
函数创建一个 GtkFixed
容器。
gtk_fixed_put(GTK_FIXED(fixed), btn1, 150, 50);
第一个按钮使用 gtk_fixed_put
函数放置在坐标 x=150 和 y=50 处。
gtk_widget_set_size_request(btn1, 80, 30);
gtk_widget_set_size_request
为控件设置最小大小。这是控件在运行良好并正确绘制自身时可以接受的最小尺寸。

GtkAlignment
GtkAlignment
控制控件的对齐方式。此外,它可以管理其缩放。
#include <gtk/gtk.h> int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *align; GtkWidget *lbl; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "GtkAlignment"); gtk_window_set_default_size(GTK_WINDOW(window), 300, 200); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_container_set_border_width(GTK_CONTAINER(window), 5); align = gtk_alignment_new(0, 1, 0, 0); lbl = gtk_label_new("bottom-left"); gtk_container_add(GTK_CONTAINER(align), lbl); gtk_container_add(GTK_CONTAINER(window), align); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; }
在示例中,标签位于窗口的左下角。
align = gtk_alignment_new(0, 1, 0, 0);
gtk_alignment_new
函数创建 GtkAlignment
容器。参数取值范围为 0 到 1。第一个参数是水平对齐,其中 0 是左对齐,1 是右对齐。第二个参数是垂直对齐,其中 0 是顶部对齐,1 是底部对齐。第三个参数是水平缩放,它是子控件水平扩展以填充未使用的空间的量。值为 0 表示子控件不应扩展。最后一个参数是垂直缩放。
lbl = gtk_label_new("bottom-left");
使用 gtk_label_new
函数创建标签控件。
gtk_container_add(GTK_CONTAINER(align), lbl);
标签添加到 GtkAlignment
容器中。
gtk_container_add(GTK_CONTAINER(window), align);
最后,对齐容器放置在窗口中。

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(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window)); gtk_widget_show_all(window); gtk_main(); return 0; }
此示例将五个按钮打包到一列中。如果我们调整应用程序窗口的大小,子控件也会调整大小。
vbox = gtk_vbox_new(TRUE, 1);
gtk_vbox_new
函数创建一个 GtkVBox
容器。我们将 homogeneous
参数设置为 TRUE
。这意味着我们所有的按钮都将具有相同的大小。控件之间的间距设置为 1 像素。
gtk_box_pack_start(GTK_BOX(vbox), settings, TRUE, TRUE, 0);
gtk_box_pack_start
函数将一个控件添加到框中。前两个参数是框容器和子控件。接下来的三个参数是 expand
、fill
和 padding
。请注意,如果 expand
参数设置为 FALSE
,则填充参数无效。同样,如果我们使用 homogeneous 参数设置为 TRUE
创建了容器,则 expand
参数无效。在我们的例子中,当窗口扩大并且控件填充额外区域时,会为“设置”按钮提供额外的空间。

GtkTable
GtkTable
控件将控件排列在行和列中。
#include <gtk/gtk.h> int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *table; GtkWidget *button; gchar *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(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; }
在此示例中,我们创建了一组我们在计算器中看到的按钮。
table = gtk_table_new(4, 4, TRUE);
我们创建一个新的 GtkTable
控件,其中包含 4 行和 4 列。当我们将 TRUE
传递给第三个参数时,所有表格单元格都会调整为包含最大控件的单元格的大小。
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 个按钮并将它们放置到容器中。gtk_table_attach_defaults
将子控件添加到具有相同填充和扩展选项的表格容器中。

角按钮
下一个示例将两个按钮放置在窗口的右下角。
#include <gtk/gtk.h> int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *okBtn; GtkWidget *clsBtn; 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), "Corner buttons"); 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); okBtn = gtk_button_new_with_label("OK"); gtk_widget_set_size_request(okBtn, 70, 30); gtk_container_add(GTK_CONTAINER(hbox), okBtn); clsBtn = gtk_button_new_with_label("Close"); gtk_container_add(GTK_CONTAINER(hbox), clsBtn); 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(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window)); gtk_widget_show_all(window); gtk_main(); return 0; }
在示例中,我们使用一个水平框、一个垂直框和两个对齐容器。
valign = gtk_alignment_new(0, 1, 0, 0);
此对齐容器将其子控件放置在底部。
gtk_container_add(GTK_CONTAINER(vbox), valign);
在这里,我们将对齐控件放入垂直框中。
hbox = gtk_hbox_new(TRUE, 3); okBtn = gtk_button_new_with_label("OK"); gtk_widget_set_size_request(okBtn, 70, 30); gtk_container_add(GTK_CONTAINER(hbox), okBtn); clsBtn = gtk_button_new_with_label("Close"); gtk_container_add(GTK_CONTAINER(hbox), clsBtn);
我们创建一个水平框并在其中放置两个按钮。gtk_widget_set_size_request
设置控件的最小尺寸。由于我们将 GtkHBox
的 homogeneous
参数设置为 TRUE
,因此另一个按钮也会调整到新尺寸。
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);
这会创建一个对齐容器,该容器将其子控件放置在右侧。我们将水平框添加到对齐容器中,并将对齐容器打包到垂直框中。对齐容器只能接受一个子控件;因此,我们还必须使用框。

Windows
接下来,我们创建一个更高级的示例。我们展示一个可以在 JDeveloper 中找到的窗口。

该对话框显示所有已打开的窗口,或者更确切地说,是 JDeveloper 应用程序中的选项卡。
#include <gtk/gtk.h> int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *table; GtkWidget *title; GtkWidget *wins; GtkWidget *halign; GtkWidget *halign2; GtkWidget *valign; GtkWidget *actBtn; GtkWidget *clsBtn; GtkWidget *hlpBtn; GtkWidget *okBtn; 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_title(GTK_WINDOW(window), "Windows"); gtk_container_set_border_width(GTK_CONTAINER(window), 15); table = gtk_table_new(6, 4, FALSE); gtk_table_set_col_spacings(GTK_TABLE(table), 3); gtk_table_set_row_spacing(GTK_TABLE(table), 0, 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); actBtn = gtk_button_new_with_label("Activate"); gtk_widget_set_size_request(actBtn, 50, 30); gtk_table_attach(GTK_TABLE(table), actBtn, 3, 4, 1, 2, GTK_FILL, GTK_SHRINK, 1, 1); valign = gtk_alignment_new(0, 0, 0, 0); clsBtn = gtk_button_new_with_label("Close"); gtk_widget_set_size_request(clsBtn, 70, 30); gtk_container_add(GTK_CONTAINER(valign), clsBtn); 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); hlpBtn = gtk_button_new_with_label("Help"); gtk_container_add(GTK_CONTAINER(halign2), hlpBtn); gtk_widget_set_size_request(hlpBtn, 70, 30); gtk_table_set_row_spacing(GTK_TABLE(table), 3, 5); gtk_table_attach(GTK_TABLE(table), halign2, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0); okBtn = gtk_button_new_with_label("OK"); gtk_widget_set_size_request(okBtn, 70, 30); gtk_table_attach(GTK_TABLE(table), okBtn, 3, 4, 4, 5, GTK_FILL, GTK_FILL, 0, 0); gtk_container_add(GTK_CONTAINER(window), table); g_signal_connect(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(6, 4, FALSE);
创建一个 GtkTable
容器。它有六行和四列。
gtk_table_set_col_spacings(GTK_TABLE(table), 3);
gtk_table_set_col_spacings
将表格中每一列之间的间距设置为 3。
gtk_table_set_row_spacing(GTK_TABLE(table), 0, 3);
gtk_table_row_spacing
设置第一行和第二行之间的间距。
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);
GtkText
控件跨越两行和两列。我们使用 gtk_text_view_set_editable
方法使该控件不可编辑,并使用 gtk_text_view_set_cursor_visible
方法隐藏其光标。
valign = gtk_alignment_new(0, 0, 0, 0); clsBtn = gtk_button_new_with_label("Close"); gtk_widget_set_size_request(clsBtn, 70, 30); gtk_container_add(GTK_CONTAINER(valign), clsBtn); 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); hlpBtn = gtk_button_new_with_label("Help"); gtk_container_add(GTK_CONTAINER(halign2), hlpBtn); gtk_widget_set_size_request(hlpBtn, 70, 30); gtk_table_set_row_spacing(GTK_TABLE(table), 3, 5); gtk_table_attach(GTK_TABLE(table), halign2, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0);
“帮助”按钮左对齐。它位于文本控件下方。我们在文本控件和按钮之间留出一些空间。
okBtn = gtk_button_new_with_label("OK"); gtk_widget_set_size_request(okBtn, 70, 30); gtk_table_attach(GTK_TABLE(table), okBtn, 3, 4, 4, 5, GTK_FILL, GTK_FILL, 0, 0);
“确定”按钮位于第二列中,位于“激活”和“关闭”按钮下方。

本章专门介绍布局管理。