====== 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**
**makefile**
all:
gcc main.c -o simple `pkg-config --cflags --libs gtk+-3.0 gthread-2.0`