首页 百科知识 应用容器的+程序

应用容器的+程序

时间:2022-10-18 百科知识 版权反馈
【摘要】:GTK+使用容器来解决界面布局的问题。对于GtkWindow而言,要放的元素可能会更多。下面结合程序实例,介绍如何在GtkWindow中应用GtkTable容器来布局程序界面。在本例中,采用表格容器,其指针变量为table。程序将检测后的反馈信息输出到多行文本框中,提示用户的回答是大于还是小于正确答案。一次猜测结束后,用户可以单击btn_start重新开始,也可以单击btn_ quit退出程序。右上角的标签显示用户猜测的累计次数。第17行声明的变量buf是用来辅助text工作的。

10.5.5 应用容器的GTK+程序

GTK+使用容器(Container)来解决界面布局的问题。所谓容器,就是可以在其中放置其他界面元素的元素。容器中放置的元素可以是可见的按钮、图标,还可以是一个容器。以继承的观点来理解,GtkWidget有一个直接的派生类———GtkContainer作为所有容器类的基类。

按照容器中可以容纳元素的个数,容器可以分为两类:

●只能容纳一个元素的容器。

●可以容纳多个元素的容器。

只能容纳一个元素的容器都继承来自GtkContainer的一个直接派生类———GtkBin,Gtk-Window、GtkButton、GtkFrame等都是GtkBin的子类,它们只能容纳一个元素。可以容纳多个元素的容器从GtkContainer直接派生,在涉及多个元素界面的时候,通常使用可以容纳多个元素的容器来进行布局管理。常用的容纳多个元素的容器有盒子容器(GtkBox)、表格容器(Gtk-Table)、隔板容器(GtkPanel)等。

在实际的程序界面中,很少有容器只需放置一个元素。一个GtkButton上面可能既要放一个图片,又要放一个文字的标识。对于GtkWindow而言,要放的元素可能会更多。那么如何在只能容纳一个元素的容器中放置多个元素呢?通常的解决方法是:首先在该容器中放入一个容器,然后在这个容器上放入需要的多个界面元素,从而间接实现放入多个元素的目的。

(1)程序概述

下面结合程序实例,介绍如何在GtkWindow中应用GtkTable容器来布局程序界面。首先看一下程序的基本功能。程序运行界面如图10-5所示。与前面的示例相比,界面元素的数量和种类都有所增加。这个界面由以下元素构成:

img510

图10-5

●3个按钮(GtkButton):它们的文字标识分别为:Start、Quit和Go;对应的指针变量是btn _start、btn_quit和btn_go。

●一个标签(GtkLabel):标签位于窗口的右上角,即btn-quit的右侧,其指针变量是label。

●一个单行输入框(GtkEntry):位于btn_start和btn_quit的下面,其指针变量是entry。

●一个可以滚动的多行文本框:它实际上由两个界面元素组合而成。多行文本框是一个文本视图(GtkTextView),其指针变量是text;使用多行文本框可以滚动的是一个滚动窗口(GtkScrolledWindow),其指针变量为scroll_win;将text放在scroll_win上面就构成了可以滚动的多行文本框。

●一个放置所有元素的窗口(GtkWindow),其指针变量是window。

以上所说的都是界面中的可见元素。除此之外,还有一个不可见的元素,这个元素就是用来关联界面布局的容器。在本例中,采用表格容器(GtkTable),其指针变量为table。除了window之外,其他元素都放进table中,再把table放进window,间接实现在window上放置多个元素的目的。

程序启动后,用户首先单击btn_start,然后就可以在提示下进行猜数的游戏了。用户在单行输入框输入一个1~100的数,然后单击btn_go,检测自己的答案。程序将检测后的反馈信息输出到多行文本框中,提示用户的回答是大于还是小于正确答案。在反复的交互中,引导用户得到正确的答案。一次猜测结束后,用户可以单击btn_start重新开始,也可以单击btn_ quit退出程序。右上角的标签显示用户猜测的累计次数。

由于程序的代码较多,将程序代码分别放到3个源文件中,其目录结果如图10-6所示。

img511

图10-6

game.c文件是定义主函数的方法,通过调用function.c中定义的函数,完成窗口的布局和事件关联。function.h是头文件,声明界面元素变量和其他函数。function.c是程序的主要部分,除主函数之外的其他所有函数都在这个文件中定义实现。

(2)代码分析

先来看一下主函数(main)的实现方法。

img512

由于程序的大部分功能都放到了function.c文件中实现,所以game.c文件很简单。主函数main中只有layout_ctrls()一条代码是需要添加的,其他代码可以看作是编写GTK+程序的常规步骤,含义与上面例子中语句的含义相同。layout_ctrls()函数是在function.h中定义的一个函数,完成创建界面、关联界面事件处理函数的功能,其实现细节稍后会介绍。

function.h的功能简单,只提供程序用到的变量和函数的声明。

img513

img514

第5、6、7行声明了3个整型变量,它们是answer(猜数的正确答案)、guess(用户的猜测) 和count(用户猜测的次数)。这些变量和其他变量都被声明为全局变量,来减少不同函数之间传递参数的数目。

第8~17行声明10个GTK+的变量,其中前9个变量在本节中介绍程序布局时已经详细说明了。注意,它们都是GtkWidget类型的指针,并不是对应的界面元素的类型,在使用它们时需要利用宏来进行类型转化。第17行声明的变量buf是用来辅助text工作的。在设置多行文本框中的文字时,需要用到文本框中GtkTextBuffer的指针。为了便于引用这个指针,引入buf变量来存储文本框的GtkTextBuffer指针。

头文件的第18~25行代码声明了7个函数的原型。这些函数可以分为3类:

●第1类有judge()、disp_count()和disp_msg() 3个函数,它们只被其他函数调用,不会被主函数main调用,可以说是私有函数。函数judge()将用户的猜测结果和正确答案比较,将比较的结果以整数的形式返回给调用者。如果猜对则返回0,小于正确答案返回-1,否则返回1.函数disp_count()完成在标签label上显示计数信息,函数dis_msg()在多行文本框中显示系统的提示信息。

●第2类函数有on_start()、on_go()和on_quit(),它们的类型相似。和第1类函数一样,这些函数不需要被主函数main()调用,是私有的。但是,通常情况下,它们也不会被其他函数调用,固定地充当事件处理函数。函数on_start()、on_go()和on_quit()分别是按钮btn_start、btn_go和btn_quit被点击的事件处理函数。

●第3类是函数layout_ctrls(),其作用在前面已经介绍过。因为它要被主函数main()调用,因此,该函数是公有的函数。

下面来看看这3类函数是如何被实现的。代码如下所示:

img515

img516

img517

第6行利用gtk_entry_get_text()函数查询单行输入文本框的text属性,转化为整数后赋值给变量guess保存。其后将用户的输入和答案比较,返回相应的比较结果:

●0:用户猜到正确答案。

●1:用户输入大于正确答案。

●-1:用户的输入小于正确答案。

第14行的g_strdup_printg()函数利用整型计数变量count得到一个计数的字符串。g_strdup_printf的使用方法和C中的printf类似,返回的字符串就是经过格式化的字符串。第15行利用得到格式化字符串设置标签label的文字信息。g_free()函数类似C中的free函数,释放由于格式化字符串而申请的空间。

程序中的界面元素虽然也是指针变量,它们通过类似“gtk_类型_new()”的函数来创建,但是这些指针并不需要开发者在代码中显式地释放,GTK+会在程序退出消息循环时完成它们的清理工作。

函数disp_msg()根据入口参数result的值,形成不同的提示信息,放到多行文本框中显示给用户。入口参数result的值和函数judge()的返回值是一致的。

第24行用到函数gtk_widget_set_sensitive(),它的作用是设置界面元素的敏感性。如果元素是敏感的,元素可以和用户交互;否则就不能使用该元素,在视觉效果上,元素就会变灰。这种敏感性(Sensitve)的属性在其他的一些工具包中也称为不活动性(Inactive)、不可用性(Disnable)或者虚幻性(Ghosted)。

第26行的函数gtk_text_buffer_insert_at_cursor()是在GtkTextBuffer的当前光标位置增加一行文字信息。函数的第1个参数是需要添加文字的GTKTextBuffer指针,第2个参数是要添加的字符串,第3个参数的长度是字符串的长度。如果长度值是-1,GTK+将根据字符串的格式加以判断。在后面会看到,buf是和多行文本框相关联的GtkTextBuffer,向这个Gtk-TextBuffer中增加字符串就是向多行文本框中增加字符串。

从代码第29行开始是事件处理函数。on_start()产生了一个1~100之间的随机整数,保存到变量answer中,同时将计数变量count设置为0。

第33行的函数gtk_text_buffer_set_text()和第26行的函数gtk_text_buffer_insert_at_cursor()的参数的意义一样,在功能上略有差别。函数gtk_text_buffer_set_text()用来设置GtkTextBuffer的文字标识,设置成功后就代替原来的文字标识,而函数gtk_text_buffer_insert_at_cursor()的功能是在原来文字标识之后增加一个新的字符串。

第34行调用私有函数,显示当前计数器的值,如果此时还没开始计数,计数器为0。

在第35行中,由于这时用户可以开始猜数,因此将按钮btn_go设置为敏感的,可以和用户交互。

函数on_quit()简单调用函数gtk_main_quit()退出消息循环,进而结束程序。

函数on_go()处理用户单击按钮btn_go的事件。首先将计数器增加1并将新的计数器值现实到标签上。然后对用户的输入进行判断,并将判断的结果传递给disp_msg()函数,形成相应的提示信息。最后,利用函数gtk_entry_set_text()将单行文本输入框清空,准备下一次的用户输入。

函数layout_ctrls()是唯一的一个需要主函数调用的接口。前面定义的所有的函数、变量都在这里被直接或间接地组合在一起,从而协调程序的所有功能。

代码第48~52行创建了最底层的window容器,并将容器的“delete_event”和GTK+函数gtk_main_quit关联起来,达到用户单击窗口关闭按钮时退出程序的效果。

第53行创建了一个表格容器table,函数gtk_table_new()的头两个参数指定表格是一个4行3列的表格。其中单行输入框占1行2列,而多行文本框占2行3列。分隔行列的表格线按照从0开始,一次递增的规律表示,在向空格容器放置界面元素时需要用到这些编号。

第56行利用gtk_container_add()函数将表格容器放到window容器中,这样只能容纳一个元素的window就可以通过table放置多个界面元素。

代码57~64行完成按钮btn_start和btn_quit的创建和关联时间处理函数的功能,并将它们放到table容器中。gtk_table_attach_defaults()函数将按钮放入table容器。它的函数原型是:

img518

其中,第1个参数是容器指针,第2个参数是放入容器的界面元素。最后四个参数指出放入table容器的位置。确定这四个参数时可以参照图10-5的布局图。参数left_attach和right_attache是元素所处表格的列边线的编号。参数top_attach和bottom_attach是元素所处表格行边线的编号。对照布局图,对于btn_start,列边线是0、1,行边线也是0、1;btn_quit由于和btn_start同处于一行,所以两者的行编号都一样,是0、1,而由于处于不同的列中,其列边线编号为1、2。

类似地,其他界面元素的位置也用这种方法推导出来的。对于table容器来说,并不是一个元素只能占用一个单元格。在本例中,单行文本entry和多行文本text都占用了多个表格。对于entry,它占用了两列,因此列编号为0、2;对于text,它占用了2行3列,因此列标号为0、3,行编号为2、4。除此之外,所有元素放入table容器的方法都一样,在后面的代码中就不再重复介绍了。

代码第65~73行创建标签label、单行输入框entry和按钮btn_go,并将事件处理函数on _go和btn_go的clicked事件关联起来。由于猜数字的答案为1~100的整数,最多有3位数字,因此在创建单行输入框entry(第67行)时,利用函数gtk_entry_new_with_max_length ()创建了一个最多接收3个字符的输入框。这样GTK+可以对输入的字符进行基本的长度验证,当用户输入的字符多于3个时,输入贯标不再右移。如果系统没有处在静音的模式下,还会发出声音提示。

最后一段代码增加了可以滚动的多行文本框。可以滚动的多行文本框是由一个多行文本框(text)和一个滚动窗口(scroll_win)组合而成的。组合方式是,首先分别创建多行文本(text)和滚动窗口(scroll_win),然后将scroll_win作为一个容器,将text添加到这个容器中即可。第77行代码用于完成将text加入scroll_win的功能。第79行得到文本框的GtkText-Buffer指针,保存到变量buf中。由于buf为全局变量,其他函数可以方便地利用buf来更改多行文本框中的文字。代码的第81行使所有界面元素可见,即让程序界面可见。

(3)编译运行

程序的编译和上一个示例的编译方法基本一致。由于本例涉及3个文件,因此为了方便定位错误,采用分块编译的方法。对照图12-4,假设当前的工作路径为guess.c所在的目录,可以采用下面的步骤进行分块编译:

①编译目录functions下面的文件:hncst@ hncst:~/game$ gcc-c functions/function.c` pkg-config gtk+-2.0--cflags--libs`

-c参数使编译到生成目标文件后就停止,编译成功后在当前工作目录中增加一个function.o文件。

②编译当前工作目录下的guess.c:hncst@ hncst:~/game$ gcc-c game.c-Ifunctions` pkg-config gtk+-2.0--cflags--libs`

由于game.c需要./functions/function.h头文件,所以使用-I参数指明额外的头文件搜索路径。编译成功后,当前工作目录下增加一个名为game.o的文件。

③将生成的两个目标文件链接成可执行文件:hncst@ hncst:~/game$ gcc game.o function.o-o game`pkg-config gtk+-2.0--cflags--libs`

使用-o参数指定生成的可执行文件的文件名为guess。编译成功后,当前目录下增加一个可执行文件game。

④执行以下命令:

hncst@ hncst:~/game$ ls

function.o functions game game.c game.o

hncst@ hncst:~/game$./game

执行成功的话,就可以看到程序运行的界面,如图10-5所示。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈