gtk多线程的程序实例

来源:互联网 发布:58上的网络销售可靠吗 编辑:程序博客网 时间:2024/06/16 17:16

采摘处:http://hi.baidu.com/03092049hg/blog/item/e0afaf136e11fb03962b437b.html


#include <gtk/gtk.h>gint test(){    while(1)       {              gdk_threads_enter();              g_printf("hello\n");              gdk_threads_leave();       };    return TRUE;}gint timeout_callback( gpointer data ){       g_thread_create(test, NULL, FALSE, NULL);       return FALSE;}/*这是一个回调函数。data参数在本示例中被忽略。*后面有更多的回调函数示例。*/void hello( GtkWidget *widget,            gpointer   data ){    g_print ("Hello World\n");}gint delete_event( GtkWidget *widget,                   GdkEvent *event,                 gpointer   data ){    /*如果你的"delete_event"信号处理函数返回FALSE,GTK会发出"destroy"信号。     *返回TRUE,你不希望关闭窗口。     *当你想弹出“你确定要退出吗?”对话框时它很有用。*/    g_print ("delete event occurred\n");    /*改TRUE为FALSE程序会关闭。*/    return TRUE;}/*另一个回调函数*/void destroy( GtkWidget *widget,              gpointer   data ){    gtk_main_quit ();}int main( int   argc,          char *argv[] ){    /* GtkWidget是构件的存储类型*/    GtkWidget *window;    GtkWidget *button;       if(!g_thread_supported()) g_thread_init(NULL);       gdk_threads_init();       /*这个函数在所有的GTK程序都要调用。参数由命令行中解析出来并且送到该程序中*/    gtk_init (&argc, &argv);       /*创建一个新窗口*/    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);       /*当窗口收到"delete_event"信号(这个信号由窗口管理器发出,通常是“关闭”     *选项或是标题栏上的关闭按钮发出的),我们让它调用在前面定义的delete_event()函数。     *传给回调函数的data参数值是NULL,它会被回调函数忽略。*/    g_signal_connect (G_OBJECT (window), "delete_event",                    G_CALLBACK (delete_event), NULL);       /*在这里我们连接"destroy"事件到一个信号处理函数。     *对这个窗口调用gtk_widget_destroy()函数或在"delete_event"回调函数中返回FALSE值     *都会触发这个事件。*/    g_signal_connect (G_OBJECT (window), "destroy",                    G_CALLBACK (destroy), NULL);       /*创建一个标签为"Hello World"的新按钮。*/    button = gtk_button_new_with_label ("Hello World");       /*当按钮收到"clicked"信号时会调用hello()函数,并将NULL传给     *它作为参数。hello()函数在前面定义了。*/    g_signal_connect (G_OBJECT (button), "clicked",                    G_CALLBACK (hello), NULL);       /*当点击按钮时,会通过调用gtk_widget_destroy(window)来关闭窗口。     * "destroy"信号会从这里或从窗口管理器发出。*/    g_signal_connect_swapped (G_OBJECT (button), "clicked",                           G_CALLBACK (gtk_widget_destroy),                              window);       /*把按钮放入窗口(一个gtk容器)中。*/    gtk_container_add (GTK_CONTAINER (window), button);       /*最后一步是显示新创建的按钮和窗口*/    gtk_widget_show (button);    gtk_widget_show (window);    gtk_timeout_add(1, timeout_callback, NULL);    /*所有的GTK程序必须有一个gtk_main()函数。程序运行停在这里     *等待事件(如键盘事件或鼠标事件)的发生。*/          gdk_threads_enter();    gtk_main ();    gdk_threads_leave();        return 0;}  #include <gtk/gtk.h>static GtkWidget *fixed;static GtkWidget *button1;static GtkWidget *button2;int running = 1;void our_thread1(GtkWidget *button){ gint x,y,towards; x=40; y=40; towards=1; while (running) {     g_usleep(1); //一定要加  gdk_threads_enter(); //在需要与图形窗口交互的时候加  gtk_fixed_move(GTK_FIXED(fixed),button,x,y);   switch(towards)  {  case 1:   x=x+10;   if (x==250) towards=2;   break;  case 2:   y=y+10;   if (y==250) towards=3;   break;  case 3:   x=x-10;   if (x==40) towards=4;   break;  case 4:   y=y-10;   if (y==50) towards=5;  }  gdk_threads_leave();  //搭配上面的 }} void on_begin(GtkWidget* button,gpointer data){     gtk_widget_set_sensitive(button,FALSE);     g_thread_create(our_thread1,button1,FALSE,NULL); }void *run_f(GtkWidget *butt,gpointer data){    running = 0;} int main(int argc,char* argv[]){     GtkWidget *window,*view;     GtkWidget *vbox,*button,*label;      if (!g_thread_supported())      g_thread_init(NULL);      gdk_threads_init();      gtk_init(&argc,&argv);     window=gtk_window_new(GTK_WINDOW_TOPLEVEL);     gtk_window_set_title(GTK_WINDOW(window),"thread apllication");     g_signal_connect(G_OBJECT(window),"delete_event",          G_CALLBACK(gtk_main_quit),NULL);     gtk_container_set_border_width(GTK_CONTAINER(window),10);     vbox=gtk_vbox_new(FALSE,0);     gtk_container_add(GTK_CONTAINER(window),vbox);     label=gtk_label_new("Notice! Button is moving");     gtk_box_pack_start(GTK_BOX(vbox),label,FALSE,FALSE,0);     view=gtk_viewport_new(NULL,NULL);     gtk_box_pack_start(GTK_BOX(vbox),view,FALSE,FALSE,0);     fixed=gtk_fixed_new();      gtk_widget_set_usize(fixed,330,330);      gtk_container_add(GTK_CONTAINER(view),fixed);     button1=gtk_button_new_with_label("1");     gtk_fixed_put(GTK_FIXED(fixed),button1,10,10);       button=gtk_button_new_with_label("Start");     gtk_box_pack_start(GTK_BOX(vbox),button,FALSE,FALSE,5);     g_signal_connect(G_OBJECT(button),"clicked",          G_CALLBACK(on_begin),NULL);                // call on_begin        GtkWidget *run = gtk_button_new_with_label("stop");     gtk_box_pack_start(GTK_BOX(vbox),run,FALSE,FALSE,5);     g_signal_connect(G_OBJECT(run),"clicked",          G_CALLBACK(run_f),NULL);                // call on_begin      gtk_widget_show_all(window);     gdk_threads_enter();     gtk_main();     gdk_threads_leave();     return FALSE;}


我们知道glib提供了一个名为g_idle_add的函数,这个函数的功能很容易理解:增加一个空闲任务,让应用程序在空闲时执行指定的函数。这种机制非常有用,如果没有这种机制,很多事情将非常麻烦。它的功能虽然简单,但并不是所有人都知道如何充分发挥它的潜力,这里说说它的几个主要用途吧。

    1.在空闲时执行低优先级任务。有的任务优先级比较低,但费耗时间比较长,像屏幕刷新等操作,我们不希望它阻碍当前操作太久,此时可以把它放到空闲任务里去做。实际上GTK+里面也是这样做的,这样可以获得更好的响应性。

    2.将同步操作异步化。我们知道在GTK+中,它使用glibsignal作为窗口/控件之间的通信方式,signal的执行是直接调用函数,即整个signal的执行过程是同步完成的。这在多数情况下工作得很好,但有时会出现重入的问题,你调我,我再调你,可能会遇到麻烦。此时我们不得不采用异步方式,而GTK+没有提供像Win32下的PostMessage之类的异步消息,幸好我们可以g_idle_add函数来模拟

    3.串行化对GUI的访问。在大多数平台下,对GUI资源的访问都是需要串行化的,即在一个GUI应用程序中,只有一个线程可以直接操作GUI资源。这是因为出于效率的考虑,GUI资源是没有加锁保护的,GTK+也是这样的。如果另外一个线程要访问GUI资源,比如要显示一条信息,怎么办呢?这可以通过g_idle_add增加一个空闲任务来实现,idle任务是GUI线程(主线程)中执行的,所以串行了对GUI资源的访问。

    这里要注意,idle任务并不是一个独立的线程或者进程,而在是主线程中执行的。所谓空闲是指,当main loop没有其它消息要处理,而且没有更高优先级的工作要做时,就认为处于空闲状态。

 

    网上各种文章都强烈建议,所有对于GUI的操作都在一个线程内完成,其他可能导致阻塞的工作在另外一个线程中。

    所以
    gdk_threads_enter();
    gtk_label_set_text(GTK_LABEL(wbus->time),text);
    gdk_threads_leave(); 
    这样的代码应该替换为:
    g_idle_add(on_finish, wbus);//
子线程中。

    gboolean on_finish(gpointer wbus)
  {
       gtk_label_set_text(GTK_LABEL(wbus->time),text);
       return FALSE;
  }







原创粉丝点击