GUI in Gtk+ con Glade & Anjuta

18
GUI in Gtk+ con Glade & Anjuta di Stefano Delfino

description

Attraverso un semplice esempio viene mostrato come utilizzare Glade e Anjuta per la realizzazione di GUI sfruttando la libreria grafica GTK e il linguaggio C.

Transcript of GUI in Gtk+ con Glade & Anjuta

Page 1: GUI in Gtk+ con Glade & Anjuta

GUI in Gtk+con

Glade &

Anjuta

di Stefano Delfino

Page 2: GUI in Gtk+ con Glade & Anjuta

Lo scopo di questa piccola guida è quello di mostrare come realizzare una semplicissima GUI utilizzando il linguaggio C e la libreria grafica Gtk+. La guida è stata realizzata utilizzando Ubuntu 10.10 “Maverick Meerkat”. Potrebbero esserci delle lievi differenze tra le varie versioni ma in linea di massima tutto dovrebbe corrispondere.

1. InstallazionePer cominciare dobbiamo installare la libreria Gtk+ ed altre componenti neccessarie per il progetto. Installate i seguenti pacchetti assecondando tutte le dipendenze:

• build-essential• libgtk2.0-dev • autotools-dev • libtool • intltool • intltool-debian• glade• anjuta

Ora abbiamo tutto ciò che ci serve per creare la nostra piccola GUI. Vogliamo creare una piccola interfaccia di dialogo che ci permette di fare login mediante l'immissione di username e password; il controllo delle credenziali inserite sarà fatto su un file di testo, ma noi siamo interessati solo all'aspetto grafico e non alle varie tecniche di autenticazione degli utenti.Per prima cosa creiamo un dialogo di richiesta credenziali.

Quella mostrata sopra è la nostra finestra di richiesta credenziali come viene mostrata in Glade a fine lavoro. Di seguito viene mostrata la finestra di risposta all'autenticazione avvenuta con successo.

Page 3: GUI in Gtk+ con Glade & Anjuta

Immaginiamo uno scenario nel quale nella prima finestra inseriamo i dati di accesso e se sono corretti apparirà la seconda finestra con su scritto l'esito dell'autenticazione positiva.

Ora che abbiamo spiegato cosa vogliamo realizzare iniziamo vedendo i due strumenti principali che andremo ad utilizzare.

2. ToolsLa maggior parte del lavoro consiste nel disegnare la parte grafica. Per velocizzare questo processo esistono programmi per lo sviluppo rapido delle interfacce grafiche i cosiddetti RAD “Rapid Application Development”. Questi programmi permettono di disegnare l'interfaccia grafica andando a costruire la struttura gerarchica che tiene assieme i vari oggetti grafici; oltre a questo permettono anche di impostare proprietà e comportamento dei vari componenti grafici. Per questa guida utilizzeremo Glade un programma RAD che ci permetterà di disegnare la nostra interfaccia grafica. Con questo programma andremo a strutturare le nostre due interfacce di dialogo componendo i vari oggetti grafici. Il programma crea un file XML che contiene la struttura statica dell'interfaccia, elencando le componenti e le proprietà definite con Glade. Andiamo a vedere Glade come si presenta graficamente.

Page 4: GUI in Gtk+ con Glade & Anjuta

Notiamo varie parti in questo programma: una Tavolozza a sinistra, con le componenti che possiamo trascinare all'interno della nostra GUI; un Ispettore in alto a destra, con la struttura gerarchica degli oggetti della nostra GUI, per il momento osserviamo che tra i widget vi è solo la finestra “window” che andremo a creare nei passi successivi; un pannello Proprietà in basso a destra, sotto l'ispettore, con le proprietà degli oggetti che selezioniamo, attraverso il quale andremo a modificare dimensioni e caratteristiche delle componenti della nostra GUI.

L'altro programma utilizzato in questa guida è Anjuta DevStudio un IDE “Integrated Development Environment” per applicazioni C/C++ e GNOME/Gtk+. Questo programma ci permette di creare e gestire l'intero progetto oltre a darci la possibilità di modificare l'interfaccia grafica direttamente dal Designer integrato.

3. Concetti teoriciPrima di continuare bisogna introdurre un po' di concetti teorici; verranno solo introdotti gli oggetti (grafici e non) che utilizzeremo ed è consigliato rileggere questa parte dopo aver completato la

Tavolozza

Ispettore

Proprietà

Page 5: GUI in Gtk+ con Glade & Anjuta

guida.Per la nostra GUI utilizzeremo l'oggetto grafico GtkWindow che ci permette di racchiudere al suo interno altri oggetti. I vari oggetti appartengono alla libreria Gtk e sono organizzati in una struttura gerarchica (potete cercare rapidamente e navigare all'interno della libreria tramite il programma DevHelp “A developers' help browser for GNOME”). In particolare distinguiamo diverse tipologie di oggetti:

Containersoggetti contenitori che servono per contenere e dare struttura alle varie componenti; utilizzeremo per tale scopo tre differenti oggetti grafici contenitori, GtkVBox, GtkHBox, GtkHButtonBox.GtkVBox e GtkHBox servono per separare e contenere verticalmente e orizzontalmente i vari oggetti, mentre GtkHButtonBox serve per contenere e separare orizzontalmente vari bottoni (per creare una pulsantiera).

Controllo e Visualizzazioneoggetti utili per l'iterazione con l'utente. Tra questi utilizzeremo GtkLabel, GtkEntry, GtkButton.

Oltre agli oggetti citati andremo ad utilizzare un oggetto non grafico GtkBuilder. Questo oggetto ci permette di descrivere un'interfaccia grafica tramite un file XML che definisce la User Interface. In altre parole a partire dal file XML, creato e modificato con Glade, è possibile creare un'interfaccia che ci consente di comunicare con l'oggetto grafico rappresentante la nostra GUI.

Segnali e gestori (callback)GTK è un toolkit guidato dagli eventi. La nostra GUI rimane in attesa, intrappolata all'interno del ciclo gtk_main, che qualcosa accada. Quando si ha un evento, come la pressione di un bottone, verrà emesso il segnale appropriato dal componente grafico “widget” interessato. Per collegare questo segnale ad un'azione appropriata, dobbiamo predisporre un gestore che catturi il segnale o i segnali emessi e chiami la funzione corretta. Come vedremo questo viene fatto con l'istruzione:

gint gtk_signal_connect ( Gtk_Object *object,gchar *name,GtkSignalFunc func,gpointer func_data );

L'istruzione riportata prende come primo argomento il widget che emetterà il segnale, come secondo il nome del segnale che si vuole catturare, come terzo la funzione che verrà invocata per gestire il segnale, e come ultimo argomento i dati che sono passati al gestore.Il valore di ritorno di tipo gint è un identificativo di questo gestore. Come si può immaginare ogni segnale può essere gestito anche con più di una funzione; le funzioni di gestione diverse verranno eseguite in sequenza. Tramite questo identificatore si ha la possibilità successivamente di rimuovere questo gestore, e lo si fa tramite il comando:

void gtk_signal_disconnect ( Gtk_Object *object,   gint id );

Passando il widget da cui si vuole rimuovere il gestore di segnale e l'identificativo restituito da una delle funzioni signal_connect, si può rimuovere il gestore di segnale che si desidera dal widget. Questa gestione dei segnali può essere fatta in modo rapido direttamente in Glade nella scheda Signals del pannello Propietà. Come vedremo più avanti basta definire solo la funzione di gestione nel nostro codice, affinché il collegamento segnale/funzione viene fatto in automatico.

Page 6: GUI in Gtk+ con Glade & Anjuta

4. Creiamo la nostra GUIIn questa parte alterneremo una sequenza di passi utilizzando i due programmi Anjuta e Glade al fine di realizzare la nostra interfaccia grafica.

Anjuta: creiamo un nuovo progetto.Aprite il programma Anjuta, e create un nuovo progetto:

• vi verrà richiesto che tipo di progetto volete creare, selezionate GTK+;• date un nome al progetto;• selezionate una destinazione per il progetto.

Osserviamo che nella cartella di destinazione data, viene creato il progetto. Per poter lanciare quanto creato (cioè Execute dal menù run o semplicemente F3) bisogna prima configurare e generare il progetto. Generiamo il progetto:

• dal menù Build premi Build Project (opp. Maiusc+F7): viene richiesto di configurare il progetto. Lasciate la voce Configuration selezionata su Debug ed andate avanti premendo Execute; (NOTA: se non viene data la possibilità di eseguire Build Project è perché bisogna configurare il progetto manualmente tramite la voce Configure Project... sotto il menu Build)

• verranno visualizzati dei messaggi, ed alla fine se tutto è andato correttamente viene visualizzato “Completed successfully”.

Notiamo che è stata creata la cartella Debug. Ora lanciamo per la prima volta quanto creato al passo precedente:

• dal menù Run premi Execute (opp. F3);• apparirà una finestra di nome “window”, e osserviamo anche i messaggi nel Terminale

dell'IDE;• per il momento non preoccupiamoci del warning “Gtk-WARNING” tra poco lo andiamo a

correggere.

Ora che abbiamo visto per la prima volta la nostra finestra, andiamo a vedere anche dove si trova nell'area del progetto. In Anjuta, nel riquadro Files, andate a vedere nella cartella src; al suo interno troviamo diversi file, ma non tutti ci interessano, solo alcuni di questi verranno da noi modificati.

Cominciamo a creare la nostra finestra di richiesta dati; premi con il tasto destro sul file *.ui e seleziona apri con Disegnatore di interfacce grafiche. Consiglio di non utilizzare il disegnatore “integrato” con Anjuta, ma quello separato installato e visto in precedenza; lo distinguiamo perché quello separato si trova sotto la linea nel menù che appare. Se non lo trovate in questo menù è perché non vi è una associazione tra il file con estensione *.ui e il disegnatore di interfacce grafiche Glade. Quindi possiamo aprirlo direttamente noi andando tramite Nautilus nella cartella /src del progetto e premendo col tasto destro, apritelo con Glade. Se abbiamo seguito attentamente e ci troviamo con quanto detto sopra vediamo apparire Glade con la nostra finestra già caricata.La figura mostrata in precedenza visualizza quanto detto; il rettangolo al centro è la nostra finestra creata in automatico nei passi precedenti. Andiamo a modificarla.

Glade: modifica della finestra di richiesta credenzialiPer cominciare selezioniamo la finestra, possiamo farlo sia dall'ispettore che premendo sull'oggetto stesso.

Nel pannello proprietà sotto la scheda General, andare a modificare i seguenti valori:• date un nome Name alla finestra, ad esempio “connectionWindow”;

Page 7: GUI in Gtk+ con Glade & Anjuta

• impostare un titolo Window Title per la finestra, ad esempio “Autenticazione”;• impostare che la finestra non è ridimensionabile Resizable impostato su no;• impostare la Posizione nella quale la finestra deve apparire, mettere Centrato;• impostare una larghezza e altezza predefinita Default Width e Height: 350 x 220;

Passate alla scheda Common ed impostare:• impostare una larghezza e altezza richiesta Request Width e Height: 350 x 220;

Sotto la scheda Signals è possibile impostare i gestori per i segnali che vogliamo gestire; ad esempio cosa vogliamo fare quando la finestra viene chiusa. La creazione automatica da parte del programma ha già provveduto per noi ad impostare il gestore destroy() per gestire la chiusura della finestra; in pratica quando il segnale di distruzione della finestra destroy sotto la voce GtkObject viene emesso, viene intercettato dal gestore destroy() che troviamo dichiarato e definito nei file callbacks.*.Se ricordiamo bene, la prima volta che abbiamo lanciato il programma, abbiamo riscontrato un “Gtk-WARNING” per via dell'argomento NULL che di default viene passato al gestore; rimuovetelo in modo da non passare nulla al gestore del segnale. Se volete provate a salvare le modifiche fatte con Glade e rilanciare da Anjuta il programma (F3) e vedete che il warning è scomparso. Basta semplicemente fare Execute (F3) che il Build (src) (F7) viene eseguito in automatico. Noterete che al posto del warning

Gtk­WARNING   **:   Could   not   lookup   object   NULL   on   signal   destroy   of   object connectionWindow

è comparso un altro messaggio

Gtk­CRITICAL **: gtk_widget_show: assertion `GTK_IS_WIDGET (widget)' failed

questo perché abbiamo modificato il nome del oggetto finestra da “window” a “connectionWindow” in Glade; quindi andiamo a modificare nel main (per il momento solo questo) la linea

window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));

in

window = GTK_WIDGET (gtk_builder_get_object (builder, "connectioWindow"));

Salvando e rilanciando (F3) il tutto notiamo che non ci sono più messaggi. Inoltre notate le modifiche apportate e cioè la finestra posizionata centralmente, il nome, la dimensione e la non ridimensionabilità della stessa.

Andiamo ad inserire altri componenti all'interno della finestra. • per prima cosa dobbiamo dividere lo spazio in parti orizzontali ne bastano 4; per fare ciò

prendere l'oggetto Vertical Box (GtkVBox) nella parte “Containers” e metterlo all'interno della finestra, selezionare il numero di elementi desiderati Number of items su 4;

• inserite nei due spazi centrali, due spazi verticali, andando quindi a trascinarvi una Horizontal Box (GtkHBox) e selezionando 2 elementi. Il risultato dovrebbe essere questo.

Page 8: GUI in Gtk+ con Glade & Anjuta

Notate la selezione fatta e nell'ispettore sul GtkVBox e come è impostata la proprietà Homogeneous. Questa rende gli spazi uguali tra loro, e la impostiamo solo sugli spazi orizzontali e non su quelli verticali, perché andremo a modificare dinamicamente il contenuto del primo spazio orizzontale, da una Label con una sola riga di testo ad una con due righe.Inseriamo altri elementi.

• prendere una Label (GtkLabel) nella parte “Control and Display” e posizionarla all'interno del primo spazio orizzontale;

• modificate nome ed etichetta alla label, mettendo Name “connectionMessage” e Label “Inserisci le credenziali d'accesso”;

• sempre sulla label impostare Format label - justification a Center;• prendere una Horizontal Button Box (GtkHButtonBox) nella parte “Containers” e

posizionarla all'interno dell'ultimo spazio orizzontale, e selezionare un numero di elementi pari a 1;

• prendere un Button (GtkButton) nella parte “Control and Display” e posizionatelo all'interno dell'unico “spazio bottone” della pulsantiera appena creata.

• modificate nome ed etichetta del bottone, mettendo Name “connectionButton” e Label “Connetti”.

Andiamo ora ad inserire la parte relativa alla Username ed alla Password.• Inserire due Label negli spazi verticali di sinistra e due Text Entry (GtkEntry) negli spazi

di destra;• modificare la Label delle due Label: quella in alto “Username” e quella in basso

“Password”;• modificate alcune proprietà delle TextEntry appena inserite, in particolare date nome

“usernameEntry” e “passwordEntry” ed una Maxmum length pari a 30;

Page 9: GUI in Gtk+ con Glade & Anjuta

• impostare nella TextEntry passwordEntry la Visibility su NO, questo per mascherare l'input con il carattere inserito in Invisible character;

Se provate a lanciare il programma noterete una leggera differenza tra le lunghezze delle TextEntry/Label corrispondenti; questo è dovuto alla lunghezza delle Label che cambia in base al proprio contenuto. Andiamo a migliorare questo aspetto estetico:

• impostare la Width request sotto la scheda Common richiesta delle Label username e password a 100;

• sotto la scheda Packing impostare Expand su NO.

Ora se visualizziamo di nuovo la nostra finestra notiamo che il tutto compare più allineato ed omogeneo.

Prima di modificare anche i segnali impostati direttamente da Glade, andiamo a vedere un po' il codice e a fare qualche modifica per collegare qualche segnale emesso dagli oggetti grafici.

Anjuta: cominciamo a modificare il codiceQuello che segue è il codice del main.c così come generato alla creazione del progetto, eccetto che per la modifica fatta al nome della finestra “connectionWindow”.

#include <gtk/gtk.h>#include "callbacks.h" 

#define UI_FILE "src/tuorialgtk.ui"  

GtkWidget* create_window (void) { 

GtkWidget *window; GtkBuilder *builder; GError* error = NULL; 

builder = gtk_builder_new (); if (!gtk_builder_add_from_file (builder, UI_FILE, &error)) { 

g_warning ("Couldn't load builder file: %s", error­>message); g_error_free (error); 

/* This is important */ gtk_builder_connect_signals (builder, NULL); window = GTK_WIDGET (gtk_builder_get_object (builder, "connectionWindow")); 

Page 10: GUI in Gtk+ con Glade & Anjuta

 g_object_unref (builder);  return window; 

int main (int argc, char *argv[]) {   GtkWidget *window; 

#ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF­8"); textdomain (GETTEXT_PACKAGE); 

#endif 

gtk_set_locale (); gtk_init (&argc, &argv); 

window = create_window (); gtk_widget_show (window); 

gtk_main (); return 0; 

}

Cercherò di commentare tutte le parti importanti di questo pezzo di codice.Per cominciare si include la libreria Gtk.

#include <gtk/gtk.h>

All'interno del main la prima cosa che si fa è l'inizializzazione della libreria per poter utilizzare le componenti grafiche Gtk.

gtk_init(&argc, &argv);

Viene resa visibile la finestra creata.

gtk_widget_show(window);

Ed infine si entra nel ciclo principale con la funzione:

gtk_main ();

Da questo ciclo si esce quando viene chiamata la funzione gtk_main_quit() che viene chiamata automaticamente con la chiusura della finestra (lo vediamo più avanti). Vediamo ora la funzione create_window().Osserviamo che in questa funzione facciamo uso della nostra finestra disegnata con Glade, UI_FILE definito sopra #define UI_FILE "src/tuorialgtk.ui". In particolare la utilizziamo utilizzando un oggetto di tipo GtkBuilder che da definizione è proprio una interfaccia per file testuali come XML. GtkBuilder è un oggetto ausiliario che legge descrizioni testuali di una interfaccia utente e istanzia l'oggetto descritto.

I passi principali per riferirci ed utilizzare una interfaccia grafica descritta tramite un file XML sono i seguenti:

Page 11: GUI in Gtk+ con Glade & Anjuta

1 ­ builder = gtk_builder_new ()2 ­ gtk_builder_add_from_file (builder, UI_FILE, &error)) 3 ­ gtk_builder_connect_signals (builder, NULL)4 ­ window = GTK_WIDGET (gtk_builder_get_object (builder, "connectionWindow"))5 ­ g_object_unref (builder)

Per prima cosa si crea un GtkBuilder con il comando gtk_builder_new(); possiamo passare una descrizione al GtkBuilder, tramite la funzione gtk_builder_add_from_file(). Un altro passo importante è il collegamento con i segnali. Se nella parte grafica abbiamo dichiarato dei gestori di risposta a segnali emessi dagli oggetti grafici, come ad esempio il nostro destroy(), per collegarli alle nostre funzioni di gestione scritte nei file callbacks.* dobbiamo chiamare un metodo gtk_builder_connect_signal() che quindi mette in corrispondenza i gestori della descrizione con i relativi gestori.

builder = gtk_builder_new (); if (!gtk_builder_add_from_file (builder, UI_FILE, &error)) { 

...} 

/* This is important */ gtk_builder_connect_signals (builder, NULL);

Per comprendere meglio l'importanza di questo comando gtk_builder_connect_signal() provate a commentarlo e a lanciare il programma. Noterete che la finestra viene distrutta ma il programma rimane nel ciclo gtk_main() questo avviene perché non vi è un'associazione tra il segnale emesso dalla finestra quando viene distrutta e la chiamata al nostro gestore destroy() e quindi alla chiamata gtk_main_quit().

1° viene premuto il bottone per chiudere la finestra.

2° l'oggetto grafico connectionWindow ha dichiarato in Glade che vuole gestireil segnale destroy di GtkObject con il gestore destroy.

3° viene cercato quindi un gestore destroy nel codice, questo grazie al comando gtk_builder_connect_signals().

4° viene quindi eseguito il comando gtk_main_quit () che oltre a chiudere la finestra tramite il gestore delle finestre esce anche dal ciclo gtk_main ().

void destroy (GtkWidget *widget, gpointer data) { 

gtk_main_quit (); }

Quella sopra riportata è la sequenza di passi eseguita se il comando gtk_builder_connect_signals() sarebbe decommentato. Quindi al momento di cercare il gestore destroy() come dichiarato sul file XML *.ui tramite Glade lo si trova correttamente nel codice eseguito. Mentre se il comando fosse commentato non vi sarebbe un collegamento tra i gestori dei segnali dichiarati in Glade e le funzioni presenti nel programma e quindi non verrebbe eseguito il comando gtk_main_quit().

Dopo aver fatto questa prova rimuovete il commento.Per rendere il programma più leggibile, rinominate il gestore destroy() in my_destroy() sia nei

Page 12: GUI in Gtk+ con Glade & Anjuta

file callbacks.* e sia in Glade (sotto la linguetta Signals nelle Proprietà dell'oggetto connectionWindow in corrispondenza del segnale GtkObject – destroy nella colonna Handler).

Con la seguente chiamata è possibile prendere un puntatore ad un oggetto dell'interfaccia grafica disegnata.

window = GTK_WIDGET (gtk_builder_get_object (builder, "connectionWindow"));

In questo caso prendiamo un riferimento alla nostra finestra “connectionWindow”. Ultimo importante passo è il comando:

g_object_unref (builder);

Con quest'ultimo ci assicuriamo che quando le referenze a questo oggetto sono diventate nulle, l'oggetto viene rilasciato, cioè la memoria occupata viene liberata.

Ora proviamo a collegare il nostro gestore rinominato in my_destroy() con il segnale emesso dal pulsante quando viene premuto. Per fare questo basta andare ad aggiungere nella funzione create_window() le seguenti istruzioni.

{...

GtkWidget *connectionButton; 

... 

connectionButton = GTK_WIDGET (gtk_builder_get_object (builder,"connectionButton")); 

gtk_signal_connect (GTK_OBJECT (connectionButton), "clicked",                     GTK_SIGNAL_FUNC (my_destroy), NULL); 

...

}

Prendiamo un riferimento al bottone e colleghiamo tramite la funzione gtk_signal_connect(), il segnale emesso dal bottone quando viene premuto, al nostro gestore. Notate il cast a GTK_OBJECT (connectionButton), il segnale “clicked” emesso dal precedente oggetto, e il nome del gestore con il relativo cast GTK_SIGNAL_FUNC (my_destroy). In questo modo la nostra finestra si chiuderà quando viene premuto il tasto “Connetti”. Salvate e lanciate il tutto e osservate se funziona.In questo esempio abbiamo visto come collegare dinamicamente con gtk_signal_connect()  un segnale al suo gestore. Nota che era possibile farlo anche da Glade andando a dichiarare my_destroy() come gestore per l'evento clicked di GtkButton, come mostrato in figura, ed omettendo quanto sopra.

Ora andiamo a collegare l'evento “bottone premuto” ad un gestore che ci stampa nel terminale le credenziali inserite; questo per mostrare la creazione di un nuovo gestore e come reperire i dati inseriti.Per prima cosa dobbiamo aggiungere la dichiarazione e definizione di un gestore che chiameremo check_data().

Page 13: GUI in Gtk+ con Glade & Anjuta

// nel file callback.h

void check_data (GtkWidget *widget, gpointer data);

// nel file callbacks.c

void check_data (GtkWidget *widget, gpointer data) {

printf(“FACCIO QUALCOSA!! \n”);}

Una volta inserito il nostro nuovo gestore lo colleghiamo al segnale “clicked” del nostro bottone.

// nel file main.c

gtk_signal_connect (GTK_OBJECT (connectionButton), "clicked",                     GTK_SIGNAL_FUNC (check_data), NULL);

Ora quando premiamo il bottone osserviamo la stampa nel Terminale.Per prendere il contenuto inserito nelle GtkEntry dobbiamo prendere dei riferimenti agli oggetti “usernameEntry” e “passwordEntry”. Per fare ciò abbiamo bisogno del nostro oggetto GtkBuilder.Possiamo procedere in diversi modi. Possiamo dichiarare il puntatore GtkBuider invece che nel metodo create_window nel nostro main() o come variabile globale; dobbiamo inoltre stare attenti all'istruzione g_object_unref(builder)  che renderebbe inutilizzabile la variabile. Ecco cosa faremo:

• dichiariamo la variabile GtkBuilder *builder nel main;• spostiamo l'istruzione g_object_unref (builder) all'uscita dal gtk_main() quindi alla

fine del nostro main();• spostiamo l'istruzione builder = gtk_builder_new() nel main prima della chiamata a

create_window();• passiamo il puntatore builder al metodo create_window (GtkBuilder *builder) {};

All'interno del gestore check_data() abbiamo bisogno del riferimento al nostro builder, per collegarci alla descrizione XML fatta con Glade e quindi prendere i riferimenti ai dati inseriti. Per fare ciò è possibile passare questo riferimento al nostro gestore al momento della connessione del segnale con esso. Più precisamente quando utilizziamo gtk_signal_connect() nella funzione create_window().

gtk_signal_connect (GTK_OBJECT (connectionButton), "clicked",                     GTK_SIGNAL_FUNC (check_data), builder);

Il nostro gestore sarà quindi in grado di referenziale tramite l'oggetto GtkBuilder le nostre due GtkEntry e prenderne il contenuto.

void check_data (GtkWidget *widget, gpointer data) { 

GtkWidget *username = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data),                                        "usernameEntry")); 

GtkWidget *password = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data),                                        "passwordEntry")); 

 gchar *usernameInput = gtk_entry_get_text(GTK_ENTRY(username)); gchar *passwordInput = gtk_entry_get_text(GTK_ENTRY(password));  

Page 14: GUI in Gtk+ con Glade & Anjuta

printf("Username: %s Password: %s \n", usernameInput, passwordInput); }

Nelle prime due istruzione vado a prendere i puntatori agli oggetti GtkEntry e nelle seguenti due ne vado a prendere il contenuto testuale. Notate il cast fatto sulla variabile “data” da gpointer a GTK_BUILDER (NB: funziona anche senza cast).Andremo ora a simulare l'autenticazione su un file di testo contente le credenziali di accesso. Create un file “account.txt” e posizionatelo nella stessa directory del progetto *.anjuta. Scrivete nel file alcune credenziali nella forma:

username1 password1username2 password2...

...

FILE *fp;

if ((fp = fopen("account.txt", "r")) == NULL) { 

printf("ERRORE fopen R"); exit(1); 

char user[30]; char pass[30]; 

int trovato = 0; 

while (fscanf(fp,"%s %s", user, pass) != EOF ) { 

if ( (strcmp(user,usernameInput) == 0) && (strcmp(pass,passwordInput) == 0) ) { 

printf(“AUTENTICATO \n”);trovato = 1;  

}} 

fclose(fp);

if (trovato == 0) {

printf(“NON AUTENTICATO \n”);}

Nel pezzo di codice riportato sopra si mostra la semplice gestione di un File testuale dalla sua lettura al confronto con il testo Username e Password inseriti sopra. La variabile “trovato” ci permette di uscire appena viene trovata una corrispondenza all'interno del file. Possiamo inserire questo codice all'interno del gestore check_data() subito dopo le due istruzioni gtk_entry_get_text().

Modifichiamo il codice per gestire il risultato dell'autenticazione nella GUI. Se i dati inseriti non sono “corretti” modifichiamo la label “Inserisci le credenziali d'accesso” in “ATTENZIONE!!\nInserire i dati correttamente”, cancellando il testo inserito nelle due Entry. Di seguito le istruzioni da inserire al posto della stampa “NON AUTENTICATO”.

GtkLabel *connectionMessage = GTK_LABEL(gtk_builder_get_object (GTK_BUILDER(data),                                        "connectionMessage")); gtk_label_set_text(connectionMessage,"Attenzione!!\nInserire i dati correttamente");

Page 15: GUI in Gtk+ con Glade & Anjuta

gtk_entry_set_text (GTK_ENTRY(gtk_builder_get_object (GTK_BUILDER(data),                              "usernameEntry")),""); gtk_entry_set_text (GTK_ENTRY(gtk_builder_get_object (GTK_BUILDER(data),                              "passwordEntry")),"");

Per prima cosa, prendiamo il riferimento alla Label contenente il messaggio di intestazione della finestra, cioè “connectionMessage”, e poi ne modifichiamo il testo utilizzando la funzione gtk_label_set_text() e cancelliamo l'eventuale testo inserito con gtk_entry_set_text().

A questo punto possiamo vedere lanciando il programma, che se i dati sono errati viene mostrata una label con un testo differente e vengono cancellati i dati inseriti. Mentre se i dati sono corretti viene stampato su terminale “AUTENTICATO” e viene ripresentata la finestra da capo. Noi a questo punto vogliamo mostrare il risultato “AUTENTICATO” su un'altra finestra per comunicarlo all'utente. Per cominciare andiamo a creare la nuova finestra, disegnandola con Glade.

Glade: creiamo la finestra risultatoAndando a modificare il nostro file *.ui, aggiungiamo una finestra. Basta premere sull'oggetto Window nella Tavolozza, tra i Toplevels. Poi:

• date alla finestra le stesse proprietà della precedente: altezza e larghezza, posizione, ridimensionabilità;

• dategli un nome “resultWindow” ed un titolo “Risultato”;• dichiarate il gestore “my_destroy” per la distruzione della finestra;• inserite all'interno della finestra una Label con nome “resultMessage” (l'etichetta non

importa andiamo a modificarla prima di visualizzare la finestra);• sempre sulla label impostare Format label - justification a Center;

Salvate il file con Glade e passiamo a modificare di nuovo il codice.

Anjuta: collegare la finestra risultatoSe l'autenticazione ha un esito positivo allora facciamo le seguenti cose:

• settiamo la label della finestra di risultato dopo aver recuperato un riferimento ad essa;• visualizziamo la finestra risultato e nascondiamo quella di connessione.

Quindi al posto dell'istruzione printf(“AUTENTICATO \n”) inseriamo le seguenti istruzioni.

GtkWidget *resultWindow;

resultWindow = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data), "resultWindow")); 

gtk_label_set_text(GTK_LABEL(gtk_builder_get_object (GTK_BUILDER(data),

Page 16: GUI in Gtk+ con Glade & Anjuta

                             "resultMessage")),"Autenticazione OK"); 

gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data),                             "connectionWindow")));  gtk_widget_show (resultWindow);

A questo punto abbiamo analizzato tutto il codice utile al nostro scopo. Di seguito si riporta integralmente le parti significative del codice dei vari file.

Main.c

#include "callbacks.h"#define UI_FILE "src/tuorialgtk.ui" 

 GtkWidget* create_window (GtkBuilder *builder) { 

GtkWidget *window; GtkWidget *connectionButton; 

GError* error = NULL; 

if (!gtk_builder_add_from_file (builder, UI_FILE, &error)) { 

g_warning ("Couldn't load builder file: %s", error­>message); g_error_free (error); 

/* This is important */ gtk_builder_connect_signals (builder, NULL); window = GTK_WIDGET (gtk_builder_get_object (builder, "connectionWindow"));  connectionButton = GTK_WIDGET (gtk_builder_get_object (builder,

                                  "connectionButton")); gtk_signal_connect (GTK_OBJECT (connectionButton), "clicked", 

                           GTK_SIGNAL_FUNC (check_data), builder);  return window; 

int main (int argc, char *argv[]) {   GtkWidget *window; 

GtkBuilder *builder; 

#ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF­8"); textdomain (GETTEXT_PACKAGE); 

#endif 

gtk_set_locale (); gtk_init (&argc, &argv); 

builder = gtk_builder_new ();  window = create_window (builder); gtk_widget_show (window); 

gtk_main ();  g_object_unref (builder); return 0; 

}

Page 17: GUI in Gtk+ con Glade & Anjuta

Callbacks.h

#include <gtk/gtk.h> 

void my_destroy (GtkWidget *widget, gpointer data); void check_data (GtkWidget *widget, gpointer data);

Callbacks.c

#include "callbacks.h" #include <stdio.h> #include <stdlib.h> #include <string.h> 

void my_destroy (GtkWidget *widget, gpointer data) { 

gtk_main_quit (); } 

void check_data (GtkWidget *widget, gpointer data) { 

GtkWidget *resultWindow; GtkWidget *username = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data),

                                        "usernameEntry")); GtkWidget *password = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data),

                                        "passwordEntry"));  gchar *usernameInput = gtk_entry_get_text(GTK_ENTRY(username)); gchar *passwordInput = gtk_entry_get_text(GTK_ENTRY(password)); 

FILE *fp;  if ((fp = fopen("account.txt", "r")) == NULL) { 

printf("ERRORE fopen R"); exit(1); 

char user[30]; char pass[30]; int trovato = 0;  while ( fscanf(fp,"%s %s", user, pass) != EOF) { 

   if ( (strcmp(user,usernameInput) == 0) &&                   (strcmp(pass,passwordInput) == 0) ) { 

resultWindow = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data),                       "resultWindow")); 

gtk_label_set_text(GTK_LABEL(gtk_builder_get_object (GTK_BUILDER(data),                              "resultMessage")),"Autenticazione OK"); 

 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER(data),

                              "connectionWindow")));  gtk_widget_show (resultWindow); 

trovato = 1;    } } 

fclose(fp); 

if (trovato == 0) { 

Page 18: GUI in Gtk+ con Glade & Anjuta

GtkLabel *connectionMessage = GTK_LABEL(gtk_builder_get_object                                           (GTK_BUILDER(data), "connectionMessage")); 

gtk_label_set_text(connectionMessage,"Attenzione!!\nInserire i dati                                                                       correttamente"); 

gtk_entry_set_text (GTK_ENTRY(gtk_builder_get_object (GTK_BUILDER(data),                                           "usernameEntry")),""); 

gtk_entry_set_text (GTK_ENTRY(gtk_builder_get_object (GTK_BUILDER(data),                                           "passwordEntry")),""); 

 } 

}

RisorseImportanti risorse dove trovare altra documentazione sono:

• http://www.micahcarrick.com/gtk-programming/ • http://library.gnome.org/devel/gtk-tutorial/stable/ • http://tadeboro.blogspot.com/2009/09/glade3-tutorial-1-introduction.html • http://www.hds619.net/blog/guida-gtk-a-puntate/ • http://anjuta.org/ • http://glade.gnome.org/