LinuxSir.cn,穿越时空的Linuxsir!

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

开线程让gtk控件显示,发生界面无响应问题 (已解决)

[复制链接]
发表于 2008-4-24 11:02:23 | 显示全部楼层 |阅读模式
需求是这样的,按一个键,启动一个任务,然后这一任务当中进度信息要通过label显示出来,让用户知道。

我现在的做法是,在按键的回调中建立一个posix线程,该线程完成一个任务,并且进度通过label显示出来,调用的是gtk_label_set_text()。线程顺利完成,label也能显示,但是退出线程后,界面就无响应了。gdb调试一直在gtk_main_loop中,没有什么有效信息。

void button_callback(...)
{
      pthread_create(...,my_task,...);
}

void *my_task(void *arg)
{
    gtk_label_set_text(...,"handle 1 start!");
    handle_1();
    gtk_label_set_text(...,"handle 1 ok!");

    gtk_label_set_text(...,"handle 2 start!");
    handle_2();
    gtk_label_set_text(...,"handle 2 ok!");


   .......

   pthread_exit(NULL);
}


大家帮我看看这个应该怎么解决呢?
发表于 2008-4-24 12:59:49 | 显示全部楼层
在工作线程中对widget进行更新,可不是什么好主意。
所有的对界面的更新都应该在主线程中进行。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-4-24 13:44:15 | 显示全部楼层
1.在gtk的程序中,主线程就是gtk_main_loop(),而这个是封装在gtk内的。gtk_label_set(),其实最后也应该是发送信号给label,让它更新。而真正把label显示出来的,也应该是在gtk_main_loop中做的,也就是主线程。可以认为我的工作线程只是发送label更新信号给主线程,让它显示。

2. 如果不建线程,直接在buton的回调里面做,是无法动态更新label的,原因是button的回调也是在gtk_main_loop()里面调用的,回调还没出来,gtk_main_loop()被阻塞,自然无法显示label。

所以建立线程,尽快出回调,在线程里更新label是很"自然"的行为。但现在发现是有问题的。

请教一下realtang,这个具体应该怎么做呢?我是不是理解错你的意思了?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-4-24 14:55:03 | 显示全部楼层
解决了,realtang版主说的对,应该在主线程里做界面的事情,我曲解了gtk_label_set()。

解决方法是用一个定时器,g_timeout_add,在其中调用gtk_label_set,工作线程只操作相关变量。
回复 支持 反对

使用道具 举报

发表于 2008-4-24 15:20:09 | 显示全部楼层
原理在这里。
GDK will call gdk_window_process_all_updates() on your behalf whenever your program returns to the main loop and becomes idle, so normally there's no need to do that manually, you just need to invalidate regions that you know should be redrawn.
GDK处理所有原始的画图,而且它默认只在程序返回到主线程并且变得空闲的时候才做这个事。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-4-24 16:14:02 | 显示全部楼层
谢谢realtang版主,现在明白了。
我现在是用g_timeout_add来做,主线程也会处理定时器的操作。

那从您上述介绍来说,是不是也可以这样做:
在工作线程中,把label的text设为我需要的内容,然后调用gtk_widget_queue_draw,等主线程空闲的时候画label。当然设置label不能使用gtk_label_set_text(),可能需要蛮力操作,这样也可行的吧?
回复 支持 反对

使用道具 举报

发表于 2008-4-24 16:43:50 | 显示全部楼层
应该是可行的
回复 支持 反对

使用道具 举报

发表于 2008-8-3 15:35:16 | 显示全部楼层
需要使用:
gdk_threads_enter();
gdk_threads_leave();
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-8-4 22:42:09 | 显示全部楼层
定时器不好,应该用g_idle_add(),里面再调用gdk_threads_enter/leave()。
回复 支持 反对

使用道具 举报

发表于 2009-2-5 18:49:18 | 显示全部楼层
我也遇到和你一样的问题...
我是进行数据库查询,在查询按钮的回调里开一个线程,打开一个dialog窗口,显示进度条,表示查询等待..查询结束则销毁该dialog窗口。

现在问题就是:
1、查询时,dialog可以显示,但界面无响应,里面的进度条也不显示,查询结束后才响应,progressbar才显示...

2、查询等待时间较长的话,主线程的界面也会失去响应。

这要如何解决呢?
回复 支持 反对

使用道具 举报

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

本版积分规则

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