c - GTK3 and multithreading, replacing deprecated functions -
i replace deprecated functions gdk_threads_enter()/leave()
in application uses threads. application now, works perfect (although not sure if right way it).
my main loop, runs gtk_main
, signal handlers. when receive start button, start thread, runs in background along main. how can update gui thread. know per documentation of gtk3 , gdk3, avoid using
gdk_threads_add_idle()
or
gdk_threads_add_timeout()
but how do if want updating done when click start? there example. not asking how use gdk_threads_add_idle()
, asking how run worker function in main without thread after clicking start.
button clicked --> start worker function "in thread previously" --> update large amount of gui elements in gui window.
you have 3 ways it:
make computation in button callback , use
gtk_event_pending()
/gtk_main_iteration()
use
g_idle_add()
or others, ,gtk_event_pending()
/gtk_main_iteration()
use thread, mutex, ,
g_idle_add()
or others. normally, mutex isn't needed may solve bugs or heisenbugs.
the third solution seems best, because first two methods, experienced problems when exiting application while computation running. application didn't exit , printing lot of "gtk critical" warnings. (i tried on windows , mingw32).
1. button callback:
if want run worker thread in main gtk loop, can directly make computation in button callback, updating gui , treating events gtk_event_pending()
, gtk_main_iteration()
, in following sample code:
void on_button_clicked(gtkbutton * button, gpointer data) { // computation... // modify gui: gtk_label_set_text(label,"text"); // run main iteration update gui, // need call these functions if gui wasn't modified, // in order responsive , treat events it: while(gtk_events_pending()) gtk_main_iteration(); // other computation... // huge computation in loop: while(1) { // computation... // update gui , treat events it: while(gtk_events_pending()) gtk_main_iteration(); } }
2. g_idle_add():
you can use, instead of g_thread_new()
, gdk_thread_add_idle()
(in case libraries not under control may use gdk_threads_enter()/leave()
) or g_idle_add()
or g_main_context_invoke()
:
gboolean compute_func(gpointer data) { // computation... // modify gui: gtk_label_set_text(label,"text"); // run main loop update gui , responsive: while(gtk_events_pending()) gtk_main_iteration(); // other computation... // huge computation in loop: while(1) { // computation... // update gui , treat events 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. thread , mutex:
in some cases using thread make computation faster, when using worker thread not in main gtk loop, , when updating gui in function added main loop gdk_threads_add_idle()
or g_idle_add()
worker thread, may have lock access gui using mutex, because there may conflict between functions accessing gui. mutex have initialized g_mutex_init(&mutex_interface);
before beeing used application. example:
gmutex mutex_interface; gboolean update_gui(gpointer data) { g_mutex_lock(&mutex_interface); // update gui here: gtk_button_set_label(button,"label"); // , read gui here, before mutex 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); // update 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); }
if need functions updating gui executed in specific order, need add 2 counters , assign number each function called g_idle_add()
or 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 gui here: gtk_button_set_label(gtk_button(d->w),d->label); // , read gui here, before mutex 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 gui: g_idle_add(update_gui,d); count++; } return null; } void on_button_clicked(gtkbutton * button, gpointer data) { g_thread_new("thread",threadcompute,button); }
i have tested case of locking individual widgets instead of whole gui, , seems work.
Comments
Post a Comment