如果要在窗口中放置多个控件,就要考虑如何编排控件的位置。GTK+提供了两种排列控件的方法:一是使用box(盒子),二是使用table(表格)。
12.5.1 使用box排列控件
1.创建和使用box容器
box是一种不可见的widget容器,它有水平排列和垂直排列两种。水平排列是控件按放入窗口的顺序水平排列,垂直排列是按控件放入窗口的顺序垂直排列。水平排列box容器使用函数gtk_hbox_new生成,而垂直排列box容器使用函数gtk_vbox_new生成。box容器生成后,使用函数gtk_box_pack_start或gtk_box_pack_end将控件放入容器中。前者由左向右、从上到下将控件放入box容器,而后者相反,由右至左,从下到上将控件放入box容器中。
下面是具体的函数定义:
Widget* gtk_hbox_new(gint homogeneous, gint spacing);
参数含义如下。
homogeneous:控制每个放入box的控件是否有同样的高或宽。
spacing:是否在控件之间填充空白。
void gtk_box_pack_start( GtkBox *box,
GtkWidget *child,
gint expand,
gint fill,
gint padding );
参数的含义如下。
l box:要放入控件的box容器。
l child:要放入box容器的控件。
l expand:是否填满box所有额外控件,TRUE表示是,如果为FALSE则该box按控件原始大小显示。gtk_hbox_new函数的参数homogeneous值为TRUE时,该参数才有效。
l fill:该值如果为TRUE,控件自行产生外控件;如果为FALSE,box在控件周围产生反白区域。只有expand为TRUE,该参数才有效。
这样的解释可能不好理解,来看一个例子程序就清楚了。在举例之前,先介绍一下按钮控件。
2.check按钮和radio按钮
生成一般的按钮有两个函数:gtk _button_new()和gtk _button_new_with_label()。前者产生一个无标签的按钮,后者生成一个有文本标签的按钮。
在开发中,也常常使用check按钮和radio按钮。它们都有两种状态,一个是选中,另外一个是未选中。所不同的是,在一组按钮中,radio按钮只能有一个被选中,其他都处于未选中状态,而check按钮没有这个限制。它们都是以双态按钮为基础的。可以使用以下函数生成一个双态按钮,第一个生成无标签按钮,第二个生成有文本标签的按钮。
GtkWidget* gtk_toggle_button_new(void);
GtkWidget* gtk_toggle_button_new_with_label(gchar *label);
对于双态按钮,经常需要在回调函数中判断按钮的状态是否被选中。方法如下:
void toggle_button_callback(GtkWidget *widget, gpointer data)
{
if(GTK_TOGGLE_BUTTON(widget)->active)
{
//按钮被选择时的处理代码
}
}
可以使用下面这个函数,设置按钮的状态:
void gtk_toggle_button_set_state(GtkToggleButton *toggle, gint state)
参数的含义如下。
l toggle:要设置状态的按钮。
l state:要设置的状态,值为TRUE把按钮设置为未选中状态,FLASE把按钮设置为选中。
生成check按钮的函数为:
GtkWidget* gtk_check_button_new(void);
GtkWidget* gtk_check _button_new_with_label(gchar *label);
生成radio按钮的函数为:
GtkWidget* gtk_radio_button_new(GSList *group);
GtkWidget* gtk_radio_button_new_with_label(GSList *group , gchar *label);
radio按钮是成组出现的,因此需要一个参数group。
例12-3 按钮控件和box容器的使用,程序名为button_box.c。
#include<gtk/gtk.h>
/*按下某个按钮后,在命令行上打印出按钮名和新的状态*/
void click_button(GtkWidget *widget,gpointer *data)
{
g_print("%s ",(char *)data);
if(GTK_TOGGLE_BUTTON(widget)->active)
g_print("state is active\n");
else
g_print("state is not active\n");
}
void destroy(GtkWidget *widget,gpointer *data)
{
gtk_main_quit();
}
int main(int argc,char **argv)
{
GtkWidget *window;
GtkWidget *box;
GSList *group;
GtkWidget *check,*radio;
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(GTK_OBJECT(window),"destroy",
GTK_SIGNAL_FUNC(destroy),NULL);
gtk_container_border_width(GTK_CONTAINER(window),50);
/*生成一个垂直box容器,并将该容器加入到主窗口中*/
box = gtk_vbox_new(FALSE,0);
gtk_container_add(GTK_CONTAINER(window),box);
/*以下生成两个check按钮,将它们加入到box容器中,并显示出来*/
check = gtk_check_button_new_with_label("coffee");
g_signal_connect(GTK_OBJECT(check),"clicked",
GTK_SIGNAL_FUNC(click_button),"check button1");
gtk_box_pack_start(GTK_BOX(box),check,TRUE,TRUE,0);
gtk_widget_show(check);
check = gtk_check_button_new_with_label("tea");
g_signal_connect(GTK_OBJECT(check),"clicked",
GTK_SIGNAL_FUNC(click_button),"check button2");
gtk_box_pack_start(GTK_BOX(box),check,TRUE,TRUE,0);
gtk_widget_show(check);
/*以下生成3个radio按钮,将它们加入到box容器中,并显示出来*/
/*
注意:生成第一个radio按钮时group参数为NULL,而后每次在该组中创建一个radio按钮都要使用gtk_radio_button_group 函数获取新的group值
*/
radio = gtk_radio_button_new_with_label(NULL,"Apple");
g_signal_connect(GTK_OBJECT(radio),"clicked",
GTK_SIGNAL_FUNC(click_button),"Apple");
gtk_box_pack_start(GTK_BOX(box),radio,TRUE,TRUE,0);
gtk_widget_show(radio);
group = gtk_radio_button_group(GTK_RADIO_BUTTON(radio));
radio = gtk_radio_button_new_with_label(group,"Banana");
g_signal_connect(GTK_OBJECT(radio),"clicked",
GTK_SIGNAL_FUNC(click_button),"Banana");
gtk_box_pack_start(GTK_BOX(box),radio,TRUE,TRUE,0);
gtk_widget_show(radio);
group = gtk_radio_button_group(GTK_RADIO_BUTTON(radio));
radio = gtk_radio_button_new_with_label(group,"Orange");
g_signal_connect(GTK_OBJECT(radio),"clicked",
GTK_SIGNAL_FUNC(click_button),"Orange");
/*将第三个radio按钮设置为选中状态*/
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio),TRUE);
gtk_box_pack_start(GTK_BOX(box),radio,TRUE,TRUE,0);
gtk_widget_show(radio);
gtk_widget_show(box);
gtk_widget_show(window);
gtk_main();
return 0;
}
运行程序后,显示如图12-3所示的界面。

图12-3 运行结果界面
12.5.2 使用table排列控件
1.创建和使用table容器
排列窗口中的控件的另一种方法是使用table(表格),可以把控件放到表格中指定的行和列中。
表格的行、列编号方法如图12-4所示。
例如,某个控件占据图12-4阴影部分,则它所占格子的坐标为left(左)0,right(右)2,top(上)1,bottom(下)3。
创建table(表格)容器的函数为:
GtkWidget* gtk_table_new( gint rows, gint columns, gint homogeneous );
参数的含义如下。
l rows:表格所占的行数。
l columns:表格所占的列数。
l homogeneous:如果其值为TRUE,表格中每个格子的大小被定义为其中最大控件的大小;如果为FALSE,则格子的宽度与最宽控件的宽度相同,高度与放入表格的最高控件相同。
将控件放入表格中,可以使用函数:
void gtk_table_attach( GtkTable *table,
GtkWidget *child,
gint left_attach,
gint right_attach,
gint top_attach,
gint buttom_attach,
gint xoptions,
gint yoptions,
gint xpadding,
gint ypadding );
参数的含义如下。
l table:要放入控件的表格。
l child:要放入表格的控件。
l left_attach、right_attach、top_attach、buttom_attach:控件在表格中的坐标。
l xoptions、yoptions:指定了选项,可以是以下值或其组合:GTK_FILL,如果控件小于它所占用的格子,控件自动扩大到它所占格子的大小。GTK_SHRINK,如果控件大于它所占用的格子,控件自动缩小到它所占格子的大小。GTK_EXPAND,表格扩展,并利用窗口中所有可用的控件。
l xpadding:指示控件与它所占格子左、右留出的空白大小,以像素表示。
l ypadding:指示控件与它所占格子上、下留出的空白大小,以像素表示。
另一个将控件放入表格的函数是:
void gtk_table_attach_defaults( GtkTable *table,
GtkWidget *child,
gint left_attach,
gint right_attach,
gint top_attach,
gint buttom_attach );
此函数参数的含义与gtk_table_attach()相同。
为了更准确地理解这些参数的含义,最好在运行例子程序时改变这些参数的值,然后查看程序显示的图形界面。
在演示表格控件的使用方法前,先介绍几个程序将会用到的控件。
2.标签控件
标签(label)控件在界面上显示一段文本。生成label控件的函数是:
GtkWidget* gtk_label_new(char *str);
生成标签控件后,修改所显示的文字可以使用下面的函数:
void gtk_label_set( GtkLabel *label, char *str );
获取当前标签所显示的文本的函数如下:
void gtk_label_get( GtkLabel *label, char **str );
3.编辑框控件
编辑框控件允许用户输入一行文本,生成编辑框的函数为:
GtkWidget* gtk_entry_new(void);
GtkWidget* gtk_entry_new_with_max_length(guint16 max);
其中,第二个函数限制了能输入到编辑框的最大字符数。
获取用户输入到编辑框架中的文本的函数是:
gchar* gtk_entry_get_text(GtkEntry *entry);
设置编辑框中的文本的函数是:
void gtk_entry_set_text( GtkEntry *entry,gchar *text );
设置能输入到编辑框中的文本最大数的函数是:
void gtk_entry_set_max_length( GtkEntry *entry,guint16 max);
设置是否允许用户向编辑框中输入文本的函数是:
void gtk_entry_set_editable( GtkEntry *entry,gboolean editable);
例12-4 表格容器、box容器、标签控件和编辑框控件的使用,程序名为box.c。
#include<gtk/gtk.h>
/*函数声明*/
GtkWidget* makeTable();
GtkWidget* makeTextEntry();
GtkWidget* makecheckButtons();
GtkWidget* makeButtonBox();
/*单击check按钮的回调函数*/
void click_button(GtkWidget *widget,gpointer *data)
{
g_print("click %s ",(char *)data);
if(GTK_TOGGLE_BUTTON(widget)->active)
g_print("and state is active\n");
else
g_print("and state is not active\n");
}
void destroy(GtkWidget *widget,gpointer *data)
{
gtk_main_quit();
}
int main(int argc,char **argv)
{
GtkWidget *window;
GtkWidget *table;
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(GTK_OBJECT(window),"destroy",
GTK_SIGNAL_FUNC(destroy),NULL);
gtk_container_border_width(GTK_CONTAINER(window),30);
table = makeTable();
gtk_container_add(GTK_CONTAINER(window),table);
gtk_widget_show(window);
gtk_main();
return 0;
}
GtkWidget* makeTable()
{
GtkWidget *table;
GtkWidget *checkButtons;
GtkWidget *textEntry;
GtkWidget *buttonBox;
/*创建table控件*/
table = gtk_table_new(2,2,FALSE);
gtk_widget_show(table);
/*创建标签和编辑框*/
textEntry = makeTextEntry();
gtk_table_attach(GTK_TABLE(table),textEntry,
0,1,0,1,
GTK_FILL|GTK_EXPAND|GTK_SHRINK,0,
0,0);
gtk_widget_show(textEntry);
/*创建4个check按钮*/
checkButtons = makecheckButtons();
gtk_table_attach(GTK_TABLE(table),checkButtons,
1,2,0,1,
GTK_FILL | GTK_EXPAND,
GTK_FILL | GTK_EXPAND,
10,0);
gtk_widget_show(checkButtons);
/*创建两个按钮*/
buttonBox = makeButtonBox();
gtk_table_attach(GTK_TABLE(table),buttonBox,
0,2,1,2,
GTK_EXPAND|GTK_FILL|GTK_SHRINK,0,
5,10);
gtk_widget_show(buttonBox);
return table;
}
GtkWidget* makeTextEntry()
{
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *text;
vbox = gtk_vbox_new(FALSE,5);
/*生成标签控件*/
label = gtk_label_new("please enter your name:");
gtk_box_pack_start(GTK_BOX(vbox),label,FALSE,FALSE,0);
gtk_widget_show(label);
/*生成编辑框*/
text = gtk_entry_new_with_max_length(15);
gtk_box_pack_start(GTK_BOX(vbox),text,FALSE,FALSE,0);
gtk_widget_show(text);
return vbox;
}
GtkWidget* makecheckButtons()
{
GtkWidget *vbox;
GtkWidget *check;
vbox = gtk_vbox_new(FALSE,0);
check = gtk_check_button_new_with_label("apple");
g_signal_connect(GTK_OBJECT(check),"clicked",
GTK_SIGNAL_FUNC(click_button),"apple");
gtk_box_pack_start(GTK_BOX(vbox),check,FALSE,FALSE,0);
gtk_widget_show(check);
check = gtk_check_button_new_with_label("banana");
g_signal_connect(GTK_OBJECT(check),"clicked",
GTK_SIGNAL_FUNC(click_button),"banana");
gtk_box_pack_start(GTK_BOX(vbox),check,FALSE,FALSE,0);
gtk_widget_show(check);
check = gtk_check_button_new_with_label("orange");
g_signal_connect(GTK_OBJECT(check),"clicked",
GTK_SIGNAL_FUNC(click_button),"orange");
gtk_box_pack_start(GTK_BOX(vbox),check,FALSE,FALSE,0);
gtk_widget_show(check);
check = gtk_check_button_new_with_label("pear");
g_signal_connect(GTK_OBJECT(check),"clicked",
GTK_SIGNAL_FUNC(click_button),"pear");
gtk_box_pack_start(GTK_BOX(vbox),check,FALSE,FALSE,0);
gtk_widget_show(check);
return vbox;
}
GtkWidget* makeButtonBox()
{
GtkWidget *hbox;
GtkWidget *button;
hbox = gtk_hbox_new(FALSE,0);
button = gtk_button_new_with_label("yes");
gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,20);
gtk_widget_show(button);
button = gtk_button_new_with_label("no");
gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,60);
gtk_widget_show(button);
return hbox;
}
程序运行后的界面如图12-5所示。

图12-5 运行结果界面






