Barra laterale

programmazione:gtk:gtk_e_i_thread

Gtk e i thread. Ovvero manipolare i widget dai thread

Autore: Fabio Di Matteo
Ultima revisione: 21/03/2017 - 10:25

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:

  1. funzione che lancia il thread;
  2. 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 <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;
}

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:

  1. funzione che lancia il thread;
  2. funzione thread;
  3. 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 <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="&lt;Inserire il valore&gt; 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`


programmazione/gtk/gtk_e_i_thread.txt · Ultima modifica: 18/04/2018 - 15:48 (modifica esterna)