我想在使用线程的应用程序中替换已弃用的函数gdk_threads_enter()/leave()
。现在的应用程序完美无缺(虽然我不确定这是否是正确的方法)。
我的主循环,运行gtk_main
和信号处理程序。当我收到一个开始按钮时,我开始一个在主要背景中运行的线程。如何从该线程更新GUI。我知道根据GTK3和GDK3的文档,他们说使用
gdk_threads_add_idle()
或
gdk_threads_add_timeout()
但是,如果我希望仅在单击“开始”时才进行更新,该怎么办呢?
有什么例子吗?我不是问如何使用gdk_threads_add_idle()
,我问的是如何在点击开始后没有线程的情况下在main中运行worker函数。
点击了按钮 - >先在线程中启动worker函数"" - >在GUI窗口中更新大量GUI元素。
答案 0 :(得分:9)
文档说的是你仍然可以在一个线程中运行你的worker函数,你不能使用该线程中的GTK和GDK函数。因此,单击“开始”时仍可以启动该线程。但是,不必从线程更新GUI元素,而是必须使用gdk_threads_add_idle()
安排从主线程更新它们。
所以你的图应该是这样的:
Main thread Worker thread
|
Button clicked
| \________
| \
| Start worker function
| |
| Computation
| |
| Want to update GUI
| |
| gdk_threads_add_idle(function1, data1)
| ______________/|
|/ |
v More computation
function1 runs |
| Want to update GUI
GUI updated |
| gdk_threads_add_idle(function2, data2)
| ______________/|
|/ |
v More computation
function2 runs |
|
etc...
如果这对于您的用例来说太复杂了,并且您的工作线程中的计算经常将控制返回到您的工作线程(比如,您在循环中计算某些内容),那么您可以完全运行计算在主线程中,没有通过简单地将控制权返回到GUI主循环来锁定GUI,如下所示:
for (lots of items) {
result = do_short_calculation_on(one_item);
update_gui(result);
while (gtk_events_pending())
gtk_main_iteration();
}
答案 1 :(得分:8)
您有3种方法可以做到:
在按钮回调中进行计算并使用
gtk_event_pending()
/gtk_main_iteration()
使用
g_idle_add()
或其他人,gtk_event_pending()
/gtk_main_iteration()
- 醇>
使用线程,最终使用互斥锁,
g_idle_add()
或其他人。通常,不需要互斥锁,但它可以解决一些错误或 Heisenbugs
第三个解决方案似乎是最好的,因为使用前两个方法,在计算运行时退出应用程序时遇到了一些问题。该应用程序没有退出并且正在打印很多" Gtk Critical "警告。 (我在Windows上尝试过它和mingw32 )。
如果要在主gtk循环中运行工作线程,可以直接在按钮回调中进行计算,更新GUI并使用gtk_event_pending()
和gtk_main_iteration()
处理来自它的事件,如在以下示例代码中:
void on_button_clicked(GtkButton * button, gpointer data) {
// do some computation...
// modify the GUI:
gtk_label_set_text(label,"text");
// run the main iteration to update the GUI,
// you need to call these functions even if the GUI wasn't modified,
// in order to get it responsive and treat events from it:
while(gtk_events_pending()) gtk_main_iteration();
// do some other computation...
// huge computation in a loop:
while(1) {
// do some computation...
// update the GUI and treat events from it:
while(gtk_events_pending()) gtk_main_iteration();
}
}
您也可以使用代替g_thread_new()
,gdk_thread_add_idle()
(如果某些不受您控制的库可能使用gdk_threads_enter()/leave()
)或g_idle_add()
或{{1} }:
g_main_context_invoke()
在使用线程的某些情况下,计算速度更快,因此在主gtk循环中使用工作线程时,以及在使用{添加到主循环的函数中更新GUI时来自工作线程的{1}}或gboolean compute_func(gpointer data) {
// do some computation...
// modify the GUI:
gtk_label_set_text(label,"text");
// run the main loop to update the GUI and get it responsive:
while(gtk_events_pending()) gtk_main_iteration();
// do some other computation...
// huge computation in a loop:
while(1) {
// do some computation...
// update GUI and treat events from it:
while(gtk_events_pending()) gtk_main_iteration();
}
return FALSE;
}
void on_button_clicked(GtkButton * button, gpointer data) {
g_idle_add(compute_func,data);
}
,您可能必须使用互斥锁来锁定对GUI的访问,因为访问GUI的函数之间可能存在冲突。在应用程序使用之前,必须使用gdk_threads_add_idle()
初始化互斥锁。例如:
g_idle_add()
如果您需要更新GUI以按特定顺序执行的功能,则需要添加两个计数器并为使用g_mutex_init(&mutex_interface);
或GMutex mutex_interface;
gboolean update_gui(gpointer data) {
g_mutex_lock(&mutex_interface);
// update the GUI here:
gtk_button_set_label(button,"label");
// And read the GUI also here, before the mutex to be unlocked:
gchar * text = gtk_entry_get_text(GTK_ENTRY(entry));
g_mutex_unlock(&mutex_interface);
return FALSE;
}
gpointer threadcompute(gpointer data) {
int count = 0;
while(count <= 10000) {
printf("\ntest %d",count);
// sometimes update the GUI:
gdk_threads_add_idle(update_gui,data);
// or:
g_idle_add(update_gui,data);
count++;
}
return NULL;
}
void on_button_clicked(GtkButton * button, gpointer data) {
g_thread_new("thread",threadcompute,data);
}
调用的每个函数分配一个数字:
g_idle_add()
我还测试了锁定单个小部件而不是整个GUI的情况,它似乎有效。
答案 2 :(得分:0)
当我关闭主窗口时出现此运行错误:Gtk-CRITICAL **:gtk_widget_get_parent:断言&#39; GTK_IS_WIDGET(小部件)&#39;失败
我想我找到了一个解决方案,使用两个全局变量来指示回调停止并调用gtk_main_quit()
,并且已经陷入了#34; destroy&#34;在以下示例中,将主窗口信号转换为名为gtk_main_quit2()
的自定义回调:
int process_running = 0; // indicate if the "process" is running
int stopprocess = 0; // indicate to the callback to stop or not
void gtk_main_quit2(GtkWidget * window, gpointer data) {
if(process_running == 0) gtk_main_quit(); // if the "process" isn't running
// then quit
stopprocess = 1; // indicate to the button callback to stop and quit
}
void on_button_clicked(GtkButton * button, gpointer data) {
// indicate the "process" is running:
process_running = 1;
// do some computation...
while(gtk_events_pending()) gtk_main_iteration();
if(stopprocess == 1) {
// if close button clicked then quit:
gtk_main_quit();
return;
}
// do some other computation...
// huge computation in a loop:
while(1) {
// do some computation...
while(gtk_events_pending()) gtk_main_iteration();
if(stopprocess == 1) {
// if close button clicked then quit:
gtk_main_quit();
return;
}
}
while(gtk_events_pending()) gtk_main_iteration();
// indicate the "process" is finished:
process_running = 0;
// in the case the user clicked close button just at the end of computation:
if(stopprocess == 1) {
gtk_main_quit();
return;
}
}
int main() {
gtk_init();
Gtkwidget * window = create_window();
g_signal_connect ((gpointer) window, "destroy", G_CALLBACK(gtk_main_quit2), NULL);
gtk_main();
}
如果您在点击关闭按钮后仍然有一些gtk警告,您可以尝试捕获&#34;删除事件&#34;信号而不是&#34;销毁&#34;主窗口上的信号。