我想在使用线程的应用程序中替换弃用的函数 gdk_threads_enter()/leave()
。现在的应用程序运行完美(虽然我不确定这是否是正确的方法)。
我的主循环,运行 gtk_main
和信号处理程序。当我收到一个开始按钮时,我会启动一个线程,该线程沿着主线程在后台运行。我如何从该线程更新 GUI。我知道根据 GTK3 和 GDK3 的文档,他们说通过使用
gdk_threads_add_idle()
或
gdk_threads_add_timeout()
但是,如果我希望仅在单击开始时才完成更新,我该怎么做呢?
有没有例子。我不是在问如何使用 gdk_threads_add_idle()
,我是在问如何在单击开始后在没有线程的情况下在 main 中运行 worker 函数。
单击按钮 --> “在先前的线程中”启动辅助功能 --> 在 GUI 窗口中更新大量 GUI 元素。
最佳答案
您有 3 种方法可以做到这一点:
make computation in the button callback and use
gtk_event_pending()
/gtk_main_iteration()
use
g_idle_add()
or others, andgtk_event_pending()
/gtk_main_iteration()
use a thread, eventually a mutex, and
g_idle_add()
or others. Normally, a mutex isn't needed but it may solve some bugs or Heisenbugs.
第三种 解决方案似乎是最好的,因为使用前两种 方法时,我在计算运行时退出应用程序时遇到了一些问题。该应用程序没有退出,并且正在打印大量“Gtk Critical”警告。 (我在 Windows 和 mingw32 上试过)。
1。按钮回调:
如果你想在主 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();
}
}
2。 g_idle_add():
您还可以使用g_thread_new()
,gdk_thread_add_idle()
(如果某些不受您控制的库可能会使用gdk_threads_enter()/leave()
) 或 g_idle_add()
或 g_main_context_invoke()
:
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);
}
3。线程和互斥:
在某些情况下,使用线程会使计算速度更快,因此当使用不在主 gtk 循环中的工作线程时,以及在添加到主循环的函数中更新 GUI 时,gdk_threads_add_idle()
或 g_idle_add()
,您可能必须使用互斥锁锁定对 GUI 的访问,因为访问 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);
}
如果您需要更新 GUI 的函数以特定顺序执行,您需要添加两个计数器并为使用 g_idle_add()
或 gdk_threads_add_ilde 调用的每个函数分配一个编号()
:
GMutex mutex_interface;
typedef struct _data DATA;
struct _data {
gchar label[1000];
GtkWidget * w;
int num;
};
int counter = 0;
int counter2 = 0;
gboolean update_gui(gpointer data) {
DATA * d = (DATA *)data;
debutloop:
g_mutex_lock(&mutex_interface);
if(d->num != counter2) {
g_mutex_unlock(&mutex_interface);
goto debutloop;
}
counter2++;
// update the GUI here:
gtk_button_set_label(GTK_BUTTON(d->w),d->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);
free(d);
return FALSE;
}
gpointer threadcompute(gpointer data) {
int count = 0;
while(count <= 10000) {
printf("\ntest %d",count);
DATA * d = (DATA*)malloc(sizeof(DATA));
sprintf(d->label,"%d",count);
d->w = (GtkWidget*)data;
d->num = counter;
counter++;
// update the GUI:
g_idle_add(update_gui,d);
count++;
}
return NULL;
}
void on_button_clicked(GtkButton * button, gpointer data) {
g_thread_new("thread",threadcompute,button);
}
我还测试了锁定单个小部件而不是整个 GUI 的情况,它似乎可以工作。
关于c - GTK3 和多线程,替换已弃用的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30607429/