In pratica faccio uso di 2 funzioni, una che lancia il thread, una che svolge il compito di incrementare la GtkProgressbar. Ricapitolando:
Di seguito il codice completo. l'esempio sottostante realizza una finestra nella quale c'è una progressbar che si aggiorna automaticamente dopo aver cliccato su “Esegui”. Nel frattempo è possibile accedere agli altri widget dell'applicazione senza alcun problema.
main.c
#include <gtk/gtk.h> #include <pthread.h> #include <unistd.h> GObject *btStart, *progressBar; ; gdouble progress; void *incrementGtkProgressBar(); void *resetGtkProgressBar(); void *incrementProgressBar() { g_print("Inside thread...\n"); progress=0.00; while(progress<1.0) { // Questa funzione viene eseguita compatibilmente // le esigenze del ciclo principale delle Gtk g_idle_add ((GSourceFunc) incrementGtkProgressBar, NULL ); progress=progress+0.10; sleep(1); } g_print("Thread completed..."); g_idle_add ((GSourceFunc) resetGtkProgressBar, NULL ); } void *incrementGtkProgressBar() { gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progressBar), progress); return FALSE; } void *resetGtkProgressBar() { gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progressBar), 0); gtk_widget_set_sensitive(GTK_WIDGET(btStart), TRUE) ; return FALSE; } //Callback che crea lancia il thread void startThread (GtkWidget *widget, gpointer data) { GThread *myThread; myThread= g_thread_new(NULL,(GThreadFunc)incrementProgressBar,data); gtk_widget_set_sensitive(widget, FALSE) ; } void on_mainWindow_delete_event(GtkWidget *widget, gpointer data) { gtk_main_quit(); } void mainWindowInit() { GError* error = NULL; gchar* glade_file = g_build_filename("gui.ui", NULL); GtkBuilder *xml; GObject *mainWindow, *entry, *lbl ; xml = gtk_builder_new (); if (!gtk_builder_add_from_file (xml, glade_file, &error)) { g_warning ("Couldn\'t load builder file: %s", error->message); g_error_free (error); } mainWindow=gtk_builder_get_object (xml,"mainWindow" ); btStart=gtk_builder_get_object (xml,"btStart" ); progressBar=gtk_builder_get_object(xml,"progressBar"); entry=gtk_builder_get_object(xml,"entry"); lbl=gtk_builder_get_object(xml,"lbl"); g_object_unref( G_OBJECT( xml ) ); g_signal_connect (mainWindow, "destroy", G_CALLBACK (on_mainWindow_delete_event), NULL); g_signal_connect (btStart, "clicked", G_CALLBACK (startThread),progressBar ); } int main (int argc, char **argv) { gtk_init (&argc, &argv); mainWindowInit(); gtk_main (); return 0; }
Come rende noto la documentazione ufficiale non è consigliabile manipolare i widget da thread (posix o glib), inquanto Gtk+ non è thread safe e potrebbero avvenire spiacevoli incidenti come il freezing della gui. Nel tentativo di far fronte a questa esigenza ho escogitato un metodo semplice per manipolare i widget dai thread in modo del tutto sicuro.
In pratica faccio uso di 3 funzioni, una che lancia il thread, una che svolge il compito senza manipolare i widget(il thread) e una terza di tipo timeout che funge da timer controllando ogni n secondi certi valori scritti dal thread e aggiorna l'interfaccia grafica. Ricapitolando:
Di seguito il codice completo. l'esempio sottostante realizza una finestra nella quale c'è una progressbar che si aggiorna automaticamente dopo aver cliccato su “Esegui”. Nel frattempo è possibile accedere agli altri widget dell'applicazione senza alcun problema.
main.c
#include <gtk/gtk.h> #include <pthread.h> #include <unistd.h> GObject *btStart ; gdouble progress; gboolean updateProgressbar(gpointer data) { g_print("UpdateProgressbar..."); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(data), progress); if (progress>=1.0) { gtk_widget_set_sensitive(GTK_WIDGET(btStart), TRUE) ; g_print("\n Stop updateProgressbar .\n"); return FALSE; } return TRUE; } //Funzione che viene avviata dal thread //La funzione aggiorna solo una variabile double, la quale viene //controllata a intervalli per aggiornare la progressbar. void *incrementProgressBar() { g_print("Inside thread...\n"); gdouble i=0.05; while(i<=1.0) { sleep(1); i=i+0.01; progress=i; } g_print("Thread completed..."); } //Callback che crea lancia il thread void startThread (GtkWidget *widget, gpointer data) { progress=0; g_timeout_add(1000, updateProgressbar, data);//lancio funzione timeout ogni 1 secondi GThread *myThread; myThread= g_thread_new(NULL,(GThreadFunc)incrementProgressBar,data); //lancio thread gtk_widget_set_sensitive(widget, FALSE) ; } void on_mainWindow_delete_event(GtkWidget *widget, gpointer data) { gtk_main_quit(); } void mainWindowInit() { GError* error = NULL; gchar* glade_file = g_build_filename("gui.ui", NULL); GtkBuilder *xml; GObject *mainWindow, *progressBar, *entry, *lbl ; xml = gtk_builder_new (); if (!gtk_builder_add_from_file (xml, glade_file, &error)) { g_warning ("Couldn\'t load builder file: %s", error->message); g_error_free (error); } mainWindow=gtk_builder_get_object (xml,"mainWindow" ); btStart=gtk_builder_get_object (xml,"btStart" ); progressBar=gtk_builder_get_object(xml,"progressBar"); entry=gtk_builder_get_object(xml,"entry"); lbl=gtk_builder_get_object(xml,"lbl"); g_object_unref( G_OBJECT( xml ) ); g_signal_connect (mainWindow, "destroy", G_CALLBACK (on_mainWindow_delete_event), NULL); g_signal_connect (btStart, "clicked", G_CALLBACK (startThread),progressBar ); } int main (int argc, char **argv) { gtk_init (&argc, &argv); mainWindowInit(); gtk_main (); return 0; }
gui.ui
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkWindow" id="mainWindow"> <property name="width_request">406</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="has_resize_grip">True</property> <signal name="delete-event" handler="on_mainWindow_delete_event()" swapped="no"/> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkEntry" id="entry"> <property name="visible">True</property> <property name="can_focus">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkLabel" id="lbl"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">Clicca su Esegui e scrivi del testo mentre avanza la GtkProgressBar. Il thread fa in modo che non siverifichino fastidiosi problemi grafici.</property> <property name="wrap">True</property> <property name="ellipsize">end</property> <property name="lines">4</property> <attributes> <attribute name="font-desc" value="<Inserire il valore> 12"/> </attributes> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkProgressBar" id="progressBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="text" translatable="yes">Progresso</property> <property name="show_text">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> <child> <placeholder/> </child> </object> </child> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">Thread Glib con Gtk</property> <property name="subtitle">Lancia un thread che incrementa la progressbar</property> <property name="show_close_button">True</property> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkButton" id="btStart"> <property name="label">gtk-execute</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <placeholder/> </child> </object> </child> </object> </child> </object> </interface>
makefile
all: gcc main.c -o simple `pkg-config --cflags --libs gtk+-3.0 gthread-2.0`