Scrivere un’applicazione in C con le GTK (Seconda parte)

Dopo una lunga attesa, ecco una nuova puntata del mio breve tutorial alla programmazione con le GTK+! Cercherò di offrirvi una panoramica sufficientemente completa sull’aspetto più importante della programmazione con le librerie GTK+: come associare delle funzioni (dette callbacks) agli eventi scatenati dai widget dell’interfaccia.

Innanzitutto ecco una schermata della nuova interfaccia della nostra piccola applicazione, sicuramente migliore di quella precedente:

La gestione degli eventi

Non mi dilungherò sulla “theory of signals and callbacks“, è un argomento molto complesso che esula dagli scopi di questo articolo, preferisco mostrare il funzionamento del sistema attraverso degli esempi pratici.

Nella funzione create_window(), all’interno del sorgente principale main.c, c’è una riga di codice, accompagnata da un commento che ne sottolinea, giustamente, l’importanza:

/* This is important */
glade_xml_signal_autoconnect (gxml);

Per capire il perchè di tale commento basta leggere cosa dice la documentazione a riguardo:

This function is a variation of glade_xml_signal_connect. It uses gmodule’s introspective features (by openning the module NULL) to look at the application’s symbol table. From here it tries to match the signal handler names given in the interface description with symbols in the application and connects the signals.

Libglade Reference Manual

Ciò significa che, mentre la funzione glade_xml_signal_connect() necessita di essere invocata per ogni evento, glade_xml_signal_autoconnect() rappresenta una soluzione automatica più elegante per legare tutti gli eventi scatenati da un widget ai gestori degli stessi.

Apriamo nuovamente il file dell’interfaccia e selezioniamo il pulsante «OK», dunque facciamo clic su «Proprietà»: in «Generale» e assegniamogli un nome secondo le nostre convenzioni preferite (io l’ho chiamato «button_ok»), infine apriamo la scheda «Segnali» e associamo al segnale «clicked» il nome di una callback che andremo a definire (il mio consiglio è di scegliere uno dei nomi presenti nell’elenco di default). Ecco una schermata:

Come assegnare la callback al segnale del widget

Ora possiamo definire il gestore nel file header callbacks.h:

void on_button_on_clicked(GtkWidget * widget, gpointer data);

Prima di implementare la funzione vi mostro quella che, solitamente, è una forma generic per le funzioni callback:

void callback_func( GtkWidget *widget,
             ... /* altri argomenti del segnale */
             gpointer   callback_data );

Il primo argomento è un puntatore al widget (finestra, pulsante, casella di testo, quello-che-è) responsabile dell’emissione del segnale (eggià, si dice proprio così…), l’ultimo è un puntatore alle informazioni passate in g_signal_connect() (se utilizzata).

Prestate attenzione al fatto che non tutte le callbacks hanno la stessa forma (i parametri dipendono dal tipo di segnale), alcune hanno delle forme molto differenti da quella mostrata nel precedente esempio.

Torniamo a noi: cosa dovrà accadere alla pressione del pulsante «OK»? Avevo pensato di stampare nell’area di testo del risultato un riepilogo dei dati inseriti dall’utente, ecco un esempio:

\

Infine, ecco il codice della callback associata all’evento, con i commenti immersi nel codice:

void 
on_button_ok_clicked (GtkWidget *widget, gpointer data) 
{ 

	GString *gstr = g_string_new(""); /* una stringa di appoggio */
	GtkTextBuffer *buffer; /* il contenuto dell'area di testo */
	 
	buffer = get_textview_result_buffer (); /* inizializziamo il puntatore... */
	 
	/* Procediamo alla formattazione della stringa del risultato... */ 
	g_string_printf(gstr, 
					"Casella di testo: %s\nPulsante numerico: %d\nCaselle di scelta: A: %d B: %d C: %d\nRighe inserite: %d\n", 
					(char *)get_entry1_text (), 
					(int)get_spinbutton1_value (), 
					(int)is_checkbutton1_selected (), 
					(int)is_checkbutton2_selected (), 
					(int)is_checkbutton3_selected (), 
					(int)get_treeview_userinput_rowscount () 
					); 
	 
	gtk_text_buffer_set_text (buffer, gstr->str, -1); /* ...infine aggiorniamo il contenuto. */
}

Per completezza presento anche il codice delle funzioni richiamate dalla callback:

/* file: main.c */

/*
 Restituisce il testo della casella di testo entry1.
*/
const gchar*
get_entry1_text()
{
	GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget (gxml, "entry1"));
	return gtk_entry_get_text(entry);
}

/*
 Restituisce il testo della casella di testo entry1.
*/
const gchar*
get_entry2_text()
{
	GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget (gxml, "entry2"));
	return gtk_entry_get_text(entry);
}

/*
 Restituisce il valore del pulsante numerico.
*/
gint get_spinbutton1_value()
{
	GtkSpinButton *spinbutton1 = (GtkSpinButton *)glade_xml_get_widget (gxml, "spinbutton1");
	return gtk_spin_button_get_value_as_int(spinbutton1);
}

/*
 Verificare che la casella di scelta sia selezionata.
*/
gboolean is_checkbutton1_selected()
{
	GtkCheckButton *checkbutton = (GtkCheckButton *)glade_xml_get_widget (gxml, "checkbutton1");
	return gtk_toggle_button_get_active ((GtkToggleButton *)checkbutton);
}

/*
 Idem
*/
gboolean is_checkbutton2_selected()
{
	GtkCheckButton *checkbutton = (GtkCheckButton *)glade_xml_get_widget (gxml, "checkbutton2");
	return gtk_toggle_button_get_active ((GtkToggleButton *)checkbutton);
}

/*
 Idem
*/
gboolean is_checkbutton3_selected()
{
	GtkCheckButton *checkbutton = (GtkCheckButton *)glade_xml_get_widget (gxml, "checkbutton3");
	return gtk_toggle_button_get_active ((GtkToggleButton *)checkbutton);
}

/*
 Conta il numero delle righe della vista ad albero.
*/
int
get_treeview_userinput_rowscount ()
{
	GtkTreeStore *store; /* puntatore al modello dei dati */
	GtkTreeIter iter; /* iteratore */
	int n = 0; /* numero delle righe */ 
	
	/* preleva il modello dalla vista ad albero */
	store = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview_userinput));
	
	/* inizializza l'iteratore con il primo elemento del modello */
	if(gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
		n+=1; /* nuova riga */
		while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter) )
			/* incrementa il contatore finchè è presente
			 un altro elemento */
			n+=1;
	}
	
	return n; /* restituisce il numero di righe */
}

/*
 Restituisce un puntatore al buffer della casella
 del risultato.
*/
GtkTextBuffer *
get_textview_result_buffer ()
{
	return gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview_result));
}

Il meccanismo può risultare non proprio immediato, ma una volta imparate le nozioni fondamentali, il resto viene tutto da sè, basta avere la pazienza di sfogliarsi la documentazione (povera solo di buoni esempi di codice) e studiarsi le funzioni necessarie ai propri scopi.

Concludo allegandovi il codice del progetto e rinnovandovi l’appuntamento alla prossima puntata, nella quale adotteremo una strategia per l’internazionalizzazione del nostro programmino.

Facebook Twitter Linkedin Plusone Pinterest Email

2 pensieri su “Scrivere un’applicazione in C con le GTK (Seconda parte)

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *