LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2117|回复: 12

线程无法刷新界面,请帮帮我

[复制链接]
发表于 2009-11-13 12:38:13 | 显示全部楼层 |阅读模式
我用GTK+创建了一个主窗口,并为窗口绑定了"expose-event"事件,在on_expose_event函数里,在窗口上显示全局变量 t 的值,另外启动了一个线程,线程的作用是,每隔半秒钟改变 t 的值,然后调用gtk_widget_queue_draw函数使主窗口刷新。
我遇到的问题是:主窗口刚显示出来时,上面显示的内容会变化,过几秒钟就不变化了,这是为什么?
注:我的ubuntu是8.10版的,在8.04版下面不存在这个问题
代码如下:

#include <gtk/gtk.h>
#include <stdio.h>

int t = 0;
GtkWidget * window = NULL;

static gboolean on_expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer data)
{
    printf("%d", t);

    cairo_t *cr;
    cr = gdk_cairo_create (widget->window);

        char sztext[64] = {0};
        sprintf(sztext, "%d", t);

        cairo_move_to (cr, 10.0, 34.0);
        cairo_show_text(cr, sztext);

        cairo_destroy (cr);

        return FALSE;
}

void * ThreadFunc(void * lpData)
{
        while (1)
        {
                g_usleep(500000);
                t++;
                printf("    %d\n", t);
                gtk_widget_queue_draw(window);
        }

        return NULL;
}

int main (int argc, char * argv[])
{
        if (!g_thread_supported())
                g_thread_init(NULL);
        gtk_init (&argc, &argv);

        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

        g_signal_connect(window, "expose-event", G_CALLBACK(on_expose_event), NULL);
        g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
        gtk_widget_set_app_paintable(window, TRUE);

        gtk_widget_show(window);

        g_thread_create(ThreadFunc, NULL, FALSE, NULL);

        gtk_main();

        return 0;
}
发表于 2009-11-13 13:39:36 | 显示全部楼层
知道了GDK_THREANS_ENTER()和GDK_THREADS_LEAVE()的用法就知道你出问题的原因了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-11-13 13:53:07 | 显示全部楼层
Post by realtang;2045367
知道了GDK_THREANS_ENTER()和GDK_THREADS_LEAVE()的用法就知道你出问题的原因了。


我试过使用GDK_THREADS_ENTER和GDK_THREADS_LEAVE(),可还是同样的现象,我在这样地方加了这两个函数
static gboolean on_expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer data)
{
printf("%d", t);
gdk_threads_enter();
cairo_t *cr;
cr = gdk_cairo_create (widget->window);

char sztext[64] = {0};
sprintf(sztext, "%d", t);

cairo_move_to (cr, 10.0, 34.0);
cairo_show_text(cr, sztext);

cairo_destroy (cr);
gdk_threads_leave();
return FALSE;
}

void * ThreadFunc(void * lpData)
{
while (1)
{
g_usleep(500000);
t++;
printf(" %d\n", t);
gdk_threads_enter();
gtk_widget_queue_draw(window);
gdk_threads_leave();
}

gdk_threads_enter();
gtk_main();
gdk_threads_leave();

请realtang再帮我一次
回复 支持 反对

使用道具 举报

发表于 2009-11-13 14:27:47 | 显示全部楼层
下面是我改的,在windows xp sp3 mingw下测试通过。
  1. #include <gtk/gtk.h>
  2. static int t = 0;
  3. static gboolean on_threads_request( void * lpData)
  4. {
  5.         gtk_widget_queue_draw( GTK_WIDGET(lpData));
  6.         return FALSE;
  7. }
  8. static gboolean on_expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer data)
  9. {
  10.         //printf("%d", t);
  11.         cairo_t *cr;
  12.         cr = gdk_cairo_create (widget->window);
  13.         char sztext[64] = {0};
  14.         sprintf(sztext, "%d", t);
  15.         cairo_move_to (cr, 10.0, 34.0);
  16.         cairo_show_text( cr, sztext);
  17.         cairo_destroy (cr);
  18.         return FALSE;
  19. }
  20. void * ThreadFunc(void * lpData)
  21. {
  22.         while (1)
  23.         {
  24.                 g_usleep(500000);
  25.                 t++;
  26.                 g_print(" %d\n", t);
  27.                 gdk_threads_enter();
  28.                 gdk_threads_add_idle( on_threads_request, lpData);
  29.                 gdk_threads_leave();
  30.         }
  31.         return NULL;
  32. }
  33. int main (int argc, char * argv[])
  34. {
  35.         if (!g_thread_supported())
  36.                 g_thread_init(NULL);
  37.         gtk_init (&argc, &argv);
  38.         GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  39.         g_signal_connect(window, "expose-event", G_CALLBACK(on_expose_event), NULL);
  40.         g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  41.         gtk_widget_set_app_paintable(window, TRUE);
  42.         gtk_widget_show(window);
  43.         g_thread_create(ThreadFunc, window, FALSE, NULL);
  44.        
  45.         gdk_threads_enter();
  46.         gtk_main();
  47.         gdk_threads_leave();
  48.        
  49.         return 0;
  50. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2009-11-13 14:37:35 | 显示全部楼层
不管什么时候, 都不要把界面的流程放到多个线程中, 界面应该被保证只在一个线程里运行, 这样可以减少很多问题, 在设计上显得更加的清晰.
另外, 你程序这样的行为, 不需要多线程, 定时器就可以满足要求.
多线程 能避免就尽量的避免吧.....
回复 支持 反对

使用道具 举报

发表于 2009-11-13 15:01:05 | 显示全部楼层
工作实践中,多线程操作界面要求不少,特别是跟网络相关的应用。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-11-13 15:17:12 | 显示全部楼层
Post by gieta;2045409
不管什么时候, 都不要把界面的流程放到多个线程中, 界面应该被保证只在一个线程里运行, 这样可以减少很多问题, 在设计上显得更加的清晰.
另外, 你程序这样的行为, 不需要多线程, 定时器就可以满足要求.
多线程 能避免就尽量的避免吧.....


这个程序只是一个模型,我要做工程序要求必须使用线程。
因为在线程中要处理大量计算的问题,如果放在主线程中做的话,程序界面就会被阻塞。
回复 支持 反对

使用道具 举报

发表于 2009-11-13 15:20:41 | 显示全部楼层
把刷新界面的任务交给定g_timeout_add或者g_idle_add()
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-11-13 15:21:11 | 显示全部楼层
realtang,高人啊,你的代码解决了我的问题。
另外还想问你一个问题,发贴时如何把代码放在下拉框里面,就像你贴代码一样。
回复 支持 反对

使用道具 举报

发表于 2009-11-14 01:48:25 | 显示全部楼层
gtk不是多线程的,多线程要gdk_thread_enter的。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表