g_timeout_add

 

在GTK中,如果您要定时让程序去作某件事,则可以使用g_timeout_add()或g_timeout_add_full().一个例子如下:

 

这个例子的作用就是把当前时间显示到窗口中,即显示了一个实时时钟。
//~~~~~~~ begin of program ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <cairo.h>
#include <gtk/gtk.h>
#include <time.h>


static char buffer[256];
/******************************
* 把buffer显示到窗口中

*  每次画窗口时调用

*/
static gboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;

cr = gdk_cairo_create(widget->window);

cairo_move_to(cr, 30, 30);
cairo_show_text(cr, buffer);

cairo_destroy(cr);

return FALSE;
}
 
/******************************
* 把当前时间打印到buffer中,并且重画窗口

*  每次timeout后调用,即每秒调用一次

*/
static gboolean
time_handler(GtkWidget *widget)
{
if (widget->window == NULL) return FALSE;

time_t curtime;
struct tm *loctime;

curtime = time(NULL);
loctime = localtime(&curtime);
strftime(buffer, 256, "%T", loctime);

gtk_widget_queue_draw(widget);
return TRUE;
}

int
main (int argc, char *argv[])
{

GtkWidget *window;
GtkWidget *darea;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);

g_signal_connect(darea, "expose-event",
G_CALLBACK(on_expose_event), NULL); // 每次画窗口时的callback
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 170, 100);

gtk_window_set_title(GTK_WINDOW(window), "timer");
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
time_handler(window);

gtk_main();

return 0;
}

//~~~~~~~ end of program ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

例子来源:

http://zetcode.com/tutorials/gtktutorial/gtkevents/

 

 

 

这两个函数的说明见下:

g_timeout_add ()

guint               g_timeout_add                       (guint interval,
GSourceFunc function,
gpointer data);

Sets a function to be called at regular intervals, with the default priority, G_PRIORITY_DEFAULT. The function is called repeatedly until it returns FALSE, at which point the timeout is automatically destroyed and the function will not be called again. The first call to the function will be at the end of the first interval.

Note that timeout functions may be delayed, due to the processing of other event sources. Thus they should not be relied on for precise timing. After each call to the timeout function, the time of the next timeout is recalculated based on the current time and the given interval (it does not try to 'catch up' time lost in delays).

 

interval : the time between calls to the function, in milliseconds (1/1000ths of a second)
function : function to call
data : data to pass to function
Returns : the ID (greater than 0) of the event source.

第一个参数是间隔的毫秒数,第二个参数是定时后的callback,第三个是传递给callback的数据。callback的形式如下:

          gint timeout_callback(gpointer data);

g_timeout_add的返回值可以用来结束这个timeout,如下(假如返回放到tag中)

          void g_source_remove(gint tag);

也可以让callback返回0或FALSE来结束timeout。

更多的参考可见GTK+tutorial 相关章节:

http://library.gnome.org/devel/gtk-tutorial/stable/c1761.html#SEC-TIMEOUTS

 

 

g_timeout_add_full ()

guint               g_timeout_add_full                  (gint priority,
guint interval,
GSourceFunc function,
gpointer data,
GDestroyNotify notify);

Sets a function to be called at regular intervals, with the given priority. The function is called repeatedly until it returns FALSE, at which point the timeout is automatically destroyed and the function will not be called again. The notify function is called when the timeout is destroyed. The first call to the function will be at the end of the first interval.

Note that timeout functions may be delayed, due to the processing of other event sources. Thus they should not be relied on for precise timing. After each call to the timeout function, the time of the next timeout is recalculated based on the current time and the given interval (it does not try to 'catch up' time lost in delays).

 

priority : the priority of the idle source. Typically this will be in the range between G_PRIORITY_DEFAULT_IDLE and G_PRIORITY_HIGH_IDLE.
interval : the time between calls to the function, in milliseconds (1/1000ths of a second)
function : function to call
data : data to pass to function
notify : function to call when the idle is removed, or NULL
Returns : the ID (greater than 0) of the event source.

 

  1. //mainloop0.c   
  2. #include<glib.h>    
  3. GMainLoop* loop;   
  4. int main(int argc, char* argv[])   
  5. {   
  6.     //g_thread_init是必需的,GMainLoop需要gthread库的支持。   
  7.     if(g_thread_supported() == 0)   
  8.         g_thread_init(NULL);   
  9.     //创建一个循环体,先不管参数的意思。   
  10.     g_print("g_main_loop_new\n");   
  11.     loop = g_main_loop_new(NULL, FALSE);   
  12.     //让这个循环体跑起来   
  13.     g_print("g_main_loop_run\n");   
  14.     g_main_loop_run(loop);   
  15.     //循环运行完成后,计数器减一   
  16.     //glib的很多结构类型和c++的智能指针相似,拥有一个计数器   
  17.     //当计数器为0时,自动释放资源。   
  18.     g_print("g_main_loop_unref\n");   
  19.     g_main_loop_unref(loop);   
  20.     return 0;   
  21. }  

 

好了,现在编译:

 

  1. gcc -g `pkg-config --cflags --libs glib-2.0 gthread-2.0` mainloop0.c -o mainloop0  

然后运行:

./mainloop0

你会发现程序会在g_main_loop_run函数阻塞,这就是glib main loop了,如果没有人通知它退出,它是不会退出的。
通知循环退出的函数是g_main_loop_quit。
怎么通知呢?主线程被g_main_loop_run阻塞了,没办法运行quit。本来我准备开一个线程,sleep一秒钟,然后调用g_main_loop_quit。不过一想我们都在学习高精尖武器了,还用土枪土炮干啥。使用glib的定时器吧~

 

  1. //mainloop1.c   
  2. #include<glib.h>    
  3. GMainLoop* loop;   
  4. gint counter = 10;   
  5. gboolean callback(gpointer arg)   
  6. {   
  7.     g_print(".");   
  8.     if(--counter ==0){   
  9.         g_print("\n");   
  10.         //退出循环   
  11.         g_main_loop_quit(loop);   
  12.         //注销定时器   
  13.         return FALSE;   
  14.     }   
  15.     //定时器继续运行   
  16.     return TRUE;   
  17. }   
  18. int main(int argc, char* argv[])   
  19. {   
  20.     if(g_thread_supported() == 0)   
  21.         g_thread_init(NULL);   
  22.     g_print("g_main_loop_new\n");   
  23.     loop = g_main_loop_new(NULL, FALSE);   
  24.     //增加一个定时器,100毫秒运行一次callback   
  25.     g_timeout_add(100,callback,NULL);   
  26.     g_print("g_main_loop_run\n");   
  27.     g_main_loop_run(loop);   
  28.     g_print("g_main_loop_unref\n");   
  29.     g_main_loop_unref(loop);   
  30.     return 0;   
  31. }  

编译运行:

 

  1. gcc -g `pkg-config --cflags --libs glib-2.0 gthread-2.0` mainloop1.c -o mainloop1   
  2. ./mainloop1  

Yeah!一秒钟后,程序正常退出了!定时器好简单!
今天到此为止。最后思考一个问题,glib的main loop主要提供给gtk使用,是gtk界面事件循环的基础,这是无可非议的。但是,在别的地方,比如我们普通的console、service程序中, 有必要用main loop么?main loop还能够应用在哪些场合?

回到前一天的问题,除了交互性界面程序,还有哪些地方适合使用glib的event loop呢?我认为答案应该是,所有需要异步操作的地方都可以用event loop。像文件、管道、设备、socket、timer、idle和其他自定义的事件都可以产生event.
要让GMainLoop能够处理这些类型的event,首先就必须把它们加到GMainLoop去。
首先我们需要了解event loop的这三个基本结构:GMainLoop, GMainContext和GSource。
它们之间的关系是这样的:
GMainLoop -> GMainContext -> {GSource1, GSource2, GSource3......}
每 个GmainLoop都包含一个GMainContext成员,而这个GMainContext成员可以装各种各样的GSource,GSource则是 具体的各种Event处理逻辑了。在这里,可以把GMainContext理解为GSource的容器。(不过它的用处不只是装GSource)
创建GMainLoop使用函数g_main_loop_new, 它的第一个参数就是需要关联的GMainContext,如果这个值为空,程序会分配一个默认的Context给GMainLoop。
把GSource加到GMainContext呢,则使用函数g_source_attach。

接下来看这个例子,它的作用是从stdin读取字符串,然后反转字符串并输出到屏幕

 

  1. //mainloop2.c   
  2. #include <glib.h>   
  3. #include <stdio.h>   
  4. #include <strings.h>    
  5. GMainLoop* loop;    
  6. //当stdin有数据可读时被GSource调用的回调函数   
  7. gboolean callback(GIOChannel *channel)   
  8. {   
  9.     gchar* str;   
  10.     gsize len;   
  11.     //从stdin读取一行字符串   
  12.     g_io_channel_read_line(channel, &str, &len, NULL, NULL);   
  13.     //去掉回车键()   
  14.     while(len > 0 && (str[len-1] == '\r' || str[len-1] == '\n'))   
  15.         str[--len]='\0';   
  16.     //反转字符串   
  17.     for(;len;len--)   
  18.         g_print("%c",str[len-1]);   
  19.     g_print("\n");   
  20.     //判断结束符   
  21.     if(strcasecmp(str, "q") == 0){   
  22.         g_main_loop_quit(loop);   
  23.     }   
  24.     g_free(str);   
  25. }    
  26. void add_source(GMainContext *context)   
  27. {   
  28.     GIOChannel* channel;   
  29.     GSource* source;   
  30.     //这里我们监视stdin是否可读, stdin的fd默认等于1   
  31.     channel = g_io_channel_unix_new(1);   
  32.     //g_io_create_watch创建一个默认的io监视作用的GSource,下次再研究自定义GSource。参数G_IO_IN表示监视stdin的读取状态。   
  33.     source = g_io_create_watch(channel, G_IO_IN);   
  34.     g_io_channel_unref(channel);   
  35.     //设置stdin可读的时候调用的回调函数   
  36.     g_source_set_callback(source, (GSourceFunc)callback, channel, NULL);   
  37.     //把GSource附加到GMainContext   
  38.     g_source_attach(source, context);   
  39.     g_source_unref(source);   
  40. }    
  41. int main(int argc, char* argv[])   
  42. {   
  43.     GMainContext *context;    
  44.     if(g_thread_supported() == 0)   
  45.         g_thread_init(NULL);   
  46.     //新建一个GMainContext   
  47.     context = g_main_context_new();   
  48.     //然后把GSource附到这个Context上   
  49.     add_source(context);   
  50.     //把Context赋给GMainLoop   
  51.     loop = g_main_loop_new(context, FALSE);    
  52.     g_print("input string('q' to quit)\n");   
  53.     g_main_loop_run(loop);    
  54.     g_main_loop_unref(loop);   
  55.     //Context用完计数器减1   
  56.     g_main_context_unref(context);    
  57.     return 0;   
  58. }  
posted @ 2011-12-12 19:43  alxe_yu  阅读(1916)  评论(0)    收藏  举报