关于GTK各个循环函数的介绍与使用
Gtk+主循环的首要目的就是在连接到X服务器的文件描述符上监听事件,并将事件转发到构件上。本节解释怎样使用主循环,怎样给主循环添加新功能:当主循环在指定的时间间隔内空闲时、当一个文件描述符已经读或写就绪、以及当主循环退出时调用一个函数。
2.10.1主循环基本知识
从根本上来说,主循环是由glib实现的。Gtk+将glib主循环连接到Gdk的X服务器,并提供一个方便的接口(glib循环是比Gtk+的循环更低层的)。
gtk_main()函数运行主循环。直到调用gtk_main_quit()函数,gtk_main()才会退出。gtk_main()函数可以 递归调用,每次调用一个gtk_main_quit()就退出gtk_main()函数的一个实例。gtk_main_level()函数返回递归的层 次,也就是:如果没有gtk_main()运行,返回0;如果一个gtk_main()函数在运行,返回1,等等。
gtk_main()函数的所有实例功能都是一样的,它们都监视同一个与X服务器的连接,都对同样的事件队列起作用。gtk_main()实例用于阻塞、遮断一个函数的控制流直到满足某些条件。
所有的Gtk+程序都用这个技巧使应用程序正在运行时main()函数不能退出去。gnome_dialog_run()函数使用了一个递归的主循环,此循环直到用户点击对话框的按钮时它才会返回。
有时候想处理一些事件,又不想将控制交给gtk_main(),可以调用gtk_main_iteration()函数对主循环进行迭代。例如,这 样可以处理单独的一个事件,它依赖于想将什么任务挂起。可以检查是否有任何事件需要通过调用gtk_events_pending()函数处理。同样地, 这两个函数允许临时将控制交还给Gtk+。例如,在一个很长的计算中,想显示一个进度条,必须允许Gtk+主循环周期性地返回,让Gtk+能重画进度条。 可以使用下面的代码:
while(gtk_events_pending())
gtk_main_iteration();
下面是有关主循环的函数:
#include<gtk/gtkmain.h>
voidgtk_main()
voidgtk_main_quit()
voidgtk_main_iteration()
gintgtk_events_pending()
guintgtk_main_level()
2.10.2退出函数
退出函数就是当调用gtk_main_quit()函数时要调用的回调函数。换句话说,回调函数只在 gtk_main()返回之前运行。回调函数应该是一个像下面这样定义的GtkFunction:
typedefgint(*GtkFunction)(gpointerdata);
退出函数是用gtk_quit_add()添加进去的。添加退出函数时,必须指定一个由gtk_main_level()返回的主循环的级别。第二 个和第三个参数指定一个回调函数和回调数据。回调函数的返回值说明了回调函数是否应该再次调用。只要回调函数返回TRUE,它会被重复调用。只要它返回 FALSE,将取消与主循环的连接,并且不会再次调用。所有的退出函数都返回FALSE时,gtk_main()会返回。
gtk_quit_add()函数返回一个ID号码,可以用于用gtk_quit_remove()函数删除该退出函数。还可以通过将回调数据传递给gtk_quit_remove_by_data()函数来删除退出函数。函数列表:退出函数
#include<gtk/gtkmain.h>
guintgtk_quit_add(guintmain_level,
GtkFunctionfunction,
gpointerdata)
voidgtk_quit_remove(guintquit_handler_id)
voidgtk_quit_remove_by_data(gpointerdata)
2.10.3Timeout函数
有时候可能想应该在gtk_main主循环中怎样让GTK做点什么。这时可以创建一个定时(Timeout)函数,隔一定时间(毫秒)就调用一次。Timeout类似于其他编程环境中的定时器控件。下面的函数用于添加一
Timeout函数。
#include<gtk/gtkmain.h>
gintgtk_timeout_add(guint32interval,
GtkFunctionfunction,
gpointerdata);
第一个参数调用定时函数的时间间隔,以毫秒计。第二个参数是要调用的函数,第三个是要传递给函数的参数。函数返回一个整数值“标志”。可以用下面的函数停止调用定时函数:
#include<gtk/gtkmain.h>
voidgtk_timeout_remove(ginttag);
其中tag参数是前一个函数返回的“标志”值。还可以让回调函数返回FALSE或0来停止调用定时函数。也就是说,要想让函数继续调用,必须让它返回一个非0值或TRUE。定期调用的回调函数声明应该是下面的形式:
ginttimeout_callback(gpointerdata);
可以看到,Timeout函数类似于许多可视化编程工具中的Timer控件(计时器)。
2.10.4idle函数
当Gtk+主循环没有其他事情做时,idle函数连续运行。只有在事件队列是空的,并且主循环正常空闲着,正等待着有什么事情发生时,idle函数 才会运行。只要它们返回TRUE,这个函数就会一次又一次地调用;当它们返回FALSE时,函数会被删除,就像调用了gtk_idle_remove() 函数一样。
列在下面函数列表中的idle函数API,与timeout以及退出函数API是一样的。不过,不能在idle函数中调用 gtk_idle_remove()函数,因为它会破坏Gtk+的函数列表。要返回FALSE来删除idle函数。idle函数在对“只此一次”的代码排 队时通常是很有用的,这样的代码一般在所有的事件已经处理之后才运行。相对昂贵(耗费系统资源较多)的操作,比如Gtk+的大小协商以及 GnomeCanvas重绘一般在返回FALSE的idle函数中发生。这保证了代价昂贵的操作只会执行一次,即使多个连续的事件独立地要求重新执行。
Gtk+的主循环包含一个简单的调度程序。idle函数有一个分配给它们的优先级,就像UNIX进程所做的一样,也可以分配一个非缺省的优先级给idle函数。
函数列表:idle函数
#include<gtk/gtkmain.h>
guintgtk_idle_add(GtkFunctionfunction,
gpointerdata)
voidgtk_idle_remove(guintidle_handler_id)
voidgtk_idle_remove_by_data(gpointerdata)
2.10.5输入函数
输入函数用于检查文件描述符的数据(由open(2)或socket(2)返回的文件描述符)。输入函数是在Gdk级处理的。当给定的文件描述符已 经读写就绪时,会调用输入函数。它们对网络应用程序特别有用。关于文件描述符请参考Unix编程方面的参考书。要添加一个输入函数,需要指定要监视的文件 描述符、要等待的状态(读或写就绪),以及一个回调函数/数据对。下面的函数列表列出了这些API。该函数可以用gdk_input_add()返回
的标识符删除。不像quit、timeout和idle函数,从输入函数里面调用gdk_input_remove()函数删除该函数是安全的,Gtk+不会处于输入函数列表的迭代状态。
要指定等待的条件,使用GdkInputCondition标志:
GDK_INPUT_READ
GDK_INPUT_WRITE
GDK_INPUT_EXCEPTION
可以用OR将一个以上的标志连在一起。这些标志对应于传到select()系统调用的三种文件描述符集。要了解详细的内容,请参考UNIX的编程参考书。如果所有条件都得到满足,就会调用输入函数。
回调函数应该是这个样子:
typedefvoid(*GdkInputFunction)(gpointerdata,
gintsource_fd,
GdkInputConditioncondition);
它接受回调数据、被监视的文件描述符以及要满足的条件(可能是一个正在监视的条件的子集)。函数列表:输入函数
#include<gdk/gdk.h>
gintgdk_input_add(gintsource_fd,
GdkInputConditioncondition,
GdkInputFunctionfunction,
gpointerdata)
voidgdk_input_remove(ginttag)
2.11编译应用程序
用前面所介绍的基本概念,已经可以编译全功能的Gtk+/Gnome应用程序了。但是还有一个大问题:如何配置编译选项?一些实用工具如automake、autoconf、libtool等,可以用来简化这一过程。
为了方便维护,同时,也是为了便于使用这些实用工具,应该在编写代码时遵从一些约定。如果要将程序发布为自由软件,最好能使程序源代码的目录结构遵 从 “GNU项目编码标准”。即使应用程序是私有的商用程序,不想公开源代码,从技术上来说,这么做也是一个非常好的选择,因为这些标准都是经过实践检验,能 够让你节省大量的时间和精力。另外还应该在程序代码中包含INSTALL、README的文件。