我想在使用线程的应用程序中替换已弃用的函数gdk_threads_enter()/leave()
。现在的应用程序工作得很完美(* 尽管我不确定这是否是正确的方法 *)。
我的主循环运行gtk_main
和信号处理程序。当我收到一个开始按钮时,我启动一个线程,该线程在后台沿着主线程运行。我怎么能从那个线程更新GUI。我知道根据GTK 3和GDK 3的文档,他们说通过使用
gdk_threads_add_idle()
或
gdk_threads_add_timeout()
但是,如果我希望仅在单击“开始”时进行更新,该如何执行此操作?有没有例子。我不是在问如何使用gdk_threads_add_idle()
,我是在问如何在没有线程的情况下在主线程中运行worker函数。
点击按钮-->启动worker函数“in thread previously”-->更新GUI窗口中的大量GUI元素。
4条答案
按热度按时间tzdcorbm1#
你有三种方法来做到这一点:
1.在按钮回调中进行计算,使用
gtk_event_pending()
/gtk_main_iteration()
1.使用
g_idle_add()
或其他,以及gtk_event_pending()
/gtk_main_iteration()
1.使用一个线程,最后是一个互斥体,以及
g_idle_add()
或其他。通常情况下,互斥是不需要的,但它可以解决一些bug或Heisenbugs。第三种解决方案似乎是最好的,因为使用前两种方法时,我在计算运行时退出应用程序时遇到了一些问题。应用程序没有退出,并且打印了很多“Gtk Critical”警告。(* 我在Windows和mingw 32 * 上试过)。
1.按钮回调:
如果要在主gtk循环中运行辅助线程,可以直接在按钮回调中进行计算,更新GUI并使用
gtk_event_pending()
和gtk_main_iteration()
处理事件,如以下示例代码所示:2. g_idle_add():
您也可以使用
gdk_thread_add_idle()
(在某些不受您控制的库可能使用gdk_threads_enter()/leave()
的情况下)或g_idle_add()
或g_main_context_invoke()
来代替g_thread_new()
:3.线程和互斥:
在 * 某些 * 情况下,使用线程可以使计算更快,因此当使用不在主gtk循环中的工作线程时,以及当使用工作线程中的
gdk_threads_add_idle()
或g_idle_add()
更新添加到主循环中的函数中的GUI时,您可能必须使用互斥锁锁定对GUI的访问,因为访问GUI的函数之间可能存在冲突。互斥体必须在被应用程序使用之前用g_mutex_init(&mutex_interface);
初始化。例如:如果需要更新GUI的函数以特定顺序执行,则需要添加两个计数器,并为每个使用
g_idle_add()
或gdk_threads_add_ilde()
调用的函数分配一个编号:我还测试了锁定单个小部件而不是整个GUI的情况,它似乎可以工作。
bvn4nwqk2#
文档中说的是,你仍然可以在线程中运行你的worker函数,只是不能在该线程中使用GTK和GDK函数。因此,您仍然可以在单击“开始”时启动线程。但是,您必须使用
gdk_threads_add_idle()
将GUI元素调度为从主线程更新,而不是从线程更新。所以你的图表应该看起来像这样:
如果这对于你的用例来说太复杂了,并且你的工作线程中有一个计算经常将控制权返回给你的工作线程(比如,你正在循环中计算一些东西),那么你可以通过短暂地将控制权返回给GUI主循环来完全在主线程中运行计算而不锁定GUI,如下所示:
af7jpaap3#
当我关闭主窗口时,我得到这个运行错误:Gtk-Critical**:gtk_widget_get_parent:Assert“GTK_IS_WIDGET(widget)”失败
我想我已经找到了一个解决方案,使用两个全局变量来指示回调停止并调用
gtk_main_quit()
,并将主窗口的“destroy”信号捕获到一个名为gtk_main_quit2()
的自定义回调中,如以下示例所示:如果你在点击关闭按钮后仍然有一些gtk警告,你可以尝试捕获“delete-event”信号而不是主窗口上的“destroy”信号。
e0bqpujr4#
这是一个老问题,但我想我会继续并添加一个更好的方法来做到这一点:
首先,Bertrand的方法1和2的问题是,在UI线程上进行长时间运行的线程是不可取的,即使调用
gtk_main_iteration()
来服务未决事件,如可以看到的,然后将其捆绑到涉及关闭和删除事件的角落情况中,并且进一步地,如果从太多的小部件进行此操作,则可能导致堆栈溢出,这些小部件都在进行长时间运行的工作。调用gtk_events_pending()
和gtk_main_iteration()
看起来就像是一个脆弱的创可贴解决方案,它可能适用于较短的操作,其中需要在快速执行某些操作的同时保持UI活动,但对于长时间运行的网络操作,这似乎不是一个好的设计模式,最好将其放入与UI完全分离的自己的线程中。现在,如果希望从这样一个长时间运行的线程更新UI,例如进行几次网络传输并报告状态,则可以使用管道进行线程间通信。使用像Bertrand的方法3中的互斥锁的问题是获取锁可能会很慢,并且如果长时间运行的线程已经获取了锁,则可能会阻塞,特别是Bertrand循环回
debutLoop
的方式,这会导致UI线程停止等待计算线程,这是不可接受的。然而,使用管道,可以以非阻塞方式与UI线程通信。
本质上,在程序开始时从FIFO文件创建一个非阻塞管道,然后可以使用
gdk_threads_add_idle
创建一个用于从后台线程接收消息的哨兵线程,如果有定时器线程经常检查URL以使用HTTP事务的结果更新UI元素,则该哨兵函数甚至可以存在于程序的整个生命周期。例如: