====== Gtk e i thread. Ovvero manipolare i widget dai thread ====== Autore: **//Fabio Di Matteo//** \\ Ultima revisione: **//21/03/2017 - 10:25//** \\ \\ {{:programmazione:gtk:thread_and_gtk.png?406|}} ===== Versione con g_idle_add ===== In pratica faccio uso di 2 funzioni, una che lancia il thread, una che svolge il compito di incrementare la GtkProgressbar. Ricapitolando: - funzione che lancia il thread; - funzione che incrementa la progressbar tramite g_idle_add ; 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 #include #include 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; } ===== Versione con g_timeout ===== 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: - funzione che lancia il thread; - funzione thread; - funzione controllore realizzata tramite i g_timeout . 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 #include #include 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** 406 True False True True False vertical True True False True 0 True False Clicca su Esegui e scrivi del testo mentre avanza la GtkProgressBar. Il thread fa in modo che non siverifichino fastidiosi problemi grafici. True end 4 False True 1 True False Progresso True False True 2 True False Thread Glib con Gtk Lancia un thread che incrementa la progressbar True True False gtk-execute True True True True False True 0 **makefile** all: gcc main.c -o simple `pkg-config --cflags --libs gtk+-3.0 gthread-2.0`