GTK3 和多线程,替换已弃用的函数
GTK3 and multithreading, replacing deprecated functions
我想在使用线程的应用程序中替换已弃用的函数 gdk_threads_enter()/leave()
。现在的应用程序运行完美(虽然我不确定这是否是正确的方法)。
我的主循环,运行是 gtk_main
和信号处理程序。当我收到一个开始按钮时,我会启动一个线程,运行s 在主线程的后台。我如何从该线程更新 GUI。我知道根据 GTK3 和 GDK3 的文档,他们说通过使用
来避免它
gdk_threads_add_idle()
或
gdk_threads_add_timeout()
但是,如果我希望只有在单击开始时才进行更新,我该怎么做呢?
有没有例子。我不是在问如何使用 gdk_threads_add_idle()
,我是在问如何 运行 在单击开始后没有线程的情况下主线程中的工作函数。
单击按钮 --> 启动 worker 函数 "in thread previously" --> 更新 GUI 中的大量 GUI 元素 window.
文档中所说的是您仍然可以 运行 在线程中使用辅助函数,只是不能从该线程使用 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...
如果这对于您的用例来说太复杂,并且您的工作线程中有一个计算 returns 经常控制您的工作线程(例如,您正在循环计算某些东西),那么您可以 运行 完全在主线程中进行计算,而无需通过将控制短暂返回到 GUI 主循环来锁定 GUI,如下所示:
for (lots of items) {
result = do_short_calculation_on(one_item);
update_gui(result);
while (gtk_events_pending())
gtk_main_iteration();
}
您有 3 种方法可以做到:
make computation in the button callback and use gtk_event_pending()
/gtk_main_iteration()
use g_idle_add()
or others, and gtk_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.
第三种解决方案似乎是最好的,因为使用前两种方法,我在退出应用程序时遇到了一些问题,同时计算是 运行ning。该应用程序没有退出,并打印出大量“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 的情况,它似乎有效。
I get this running error when i close the main window: Gtk-CRITICAL
**: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
我想我找到了一个解决方案,使用两个全局变量指示回调停止和调用 gtk_main_quit()
,并捕获主要 window 的 "destroy" 信号] 在以下示例中转换为名为 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 警告,您可以尝试捕获 "delete-event" 信号而不是主 window 上的 "destroy" 信号。
我想在使用线程的应用程序中替换已弃用的函数 gdk_threads_enter()/leave()
。现在的应用程序运行完美(虽然我不确定这是否是正确的方法)。
我的主循环,运行是 gtk_main
和信号处理程序。当我收到一个开始按钮时,我会启动一个线程,运行s 在主线程的后台。我如何从该线程更新 GUI。我知道根据 GTK3 和 GDK3 的文档,他们说通过使用
gdk_threads_add_idle()
或
gdk_threads_add_timeout()
但是,如果我希望只有在单击开始时才进行更新,我该怎么做呢?
有没有例子。我不是在问如何使用 gdk_threads_add_idle()
,我是在问如何 运行 在单击开始后没有线程的情况下主线程中的工作函数。
单击按钮 --> 启动 worker 函数 "in thread previously" --> 更新 GUI 中的大量 GUI 元素 window.
文档中所说的是您仍然可以 运行 在线程中使用辅助函数,只是不能从该线程使用 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...
如果这对于您的用例来说太复杂,并且您的工作线程中有一个计算 returns 经常控制您的工作线程(例如,您正在循环计算某些东西),那么您可以 运行 完全在主线程中进行计算,而无需通过将控制短暂返回到 GUI 主循环来锁定 GUI,如下所示:
for (lots of items) {
result = do_short_calculation_on(one_item);
update_gui(result);
while (gtk_events_pending())
gtk_main_iteration();
}
您有 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.
第三种解决方案似乎是最好的,因为使用前两种方法,我在退出应用程序时遇到了一些问题,同时计算是 运行ning。该应用程序没有退出,并打印出大量“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 的情况,它似乎有效。
I get this running error when i close the main window: Gtk-CRITICAL **: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
我想我找到了一个解决方案,使用两个全局变量指示回调停止和调用 gtk_main_quit()
,并捕获主要 window 的 "destroy" 信号] 在以下示例中转换为名为 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 警告,您可以尝试捕获 "delete-event" 信号而不是主 window 上的 "destroy" 信号。