vai alla Homepage di Apogeonline

 



Cos'è OpenPress
Glossario
Linux-FAQ
Documenti:

Open Source Definition

GNU General Public License

La cattedrale e il bazaar

Colonizzare la noosfera

Il calderone magico


Libri:

Italian crackdown

Open Sources

MediaMorfosi

GTK+/GNOME
sviluppo applicazioni


Telematica per la pace

Linux HOWTO: Installazione e configurazione

Linux HOWTO: Networking


Risorse
Feedback
vai alla Homepage di Apogeo Editore

Vai alla homepage di OpenPress

GTK+ / Gnome Sviluppo di Applicazioni


Fondamenti di GTK+

Questo capitolo espone la creazione di un tipico "Hello, World!" per fornire una visione generale di GTK+. La discussione si sposta successivamente su alcuni dettagli essenziali da conoscere per la creazione di applicazioni GTK+.

Se avete già letto il Tutorial di GTK+ a http://www.gtk.org/ , oppure il libro Developing Linux Applications with Gtk+ and Gdk, potete eventualmente tralasciare questo capitolo e andare oltre. Se non avete mai utilizzato GTK+ prima d'ora, fate molta attenzione: questo capitolo scorre molto velocemente, quindi leggete con cura.

GTK+

In GTK+, lo stile di scrittura orientato agli oggetti, il design chiaro e una convenzione sulla denominazione della API seguita scrupolosamente rendono gli applicativi facili da scrivere e semplici da capire. Passando direttamente alla pratica, ecco un "Hello, World!" in GTK+. Potrete indovinare la funzione di circa l'80% del codice qui esposto anche se non avete mai visto GTK+ prima di adesso.

Un "Hello World" completo

Figura 1. Hello, World!


#include <gtk/gtk.h>

static gint delete_event_cb(GtkWidget* w, GdkEventAny* e, gpointer data);
static void button_click_cb(GtkWidget* w, gpointer data);

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  button = gtk_button_new();

  label  = gtk_label_new("Hello, World!!");

  gtk_container_add(GTK_CONTAINER(button), label);
  gtk_container_add(GTK_CONTAINER(window), button);

  gtk_window_set_title(GTK_WINDOW(window), "Hello, World!");
  gtk_container_set_border_width(GTK_CONTAINER(button), 10);

  gtk_signal_connect(GTK_OBJECT(window),
                     "delete_event",
                     GTK_SIGNAL_FUNC(delete_event_cb),
                     NULL);

  gtk_signal_connect(GTK_OBJECT(button),
                     "clicked",
                     GTK_SIGNAL_FUNC(button_click_cb),
                     label);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

static gint
delete_event_cb(GtkWidget* window, GdkEventAny* e, gpointer data)
{
  gtk_main_quit();
  return FALSE;
}

static void
button_click_cb(GtkWidget* w, gpointer data)
{
  GtkWidget* label;
  gchar* text;
  gchar* tmp;

  label = GTK_WIDGET(data);

  gtk_label_get(GTK_LABEL(label), &text);

  tmp = g_strdup(text);

  g_strreverse(tmp);

  gtk_label_set_text(GTK_LABEL(label), tmp);

  g_free(tmp);
}



Compilare Hello, World

GTK+ fornisce uno script di shell chiamato gtk-config. Questo viene creato al momento della compilazione di GTK+, e il suo fine è quello di fornire le opzioni per il compilatore necessarie a compilare applicazioni GTK+. La seguente sessione di shell mostra le sue caratteristiche:


$ gtk-config --version
1.2.5
$ gtk-config --prefix
/cgabriel/gtk
$ gtk-config --exec-prefix
/cgabriel/gtk
$ gtk-config --libs
-L/cgabriel/gtk/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib -ldl -lXext -lX11 -lm
$ gtk-config --libs gthread
-L/cgabriel/gtk/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lgthread -lglib -lpthread -ldl -lXext -lX11 -lm
$ gtk-config --cflags
-I/usr/X11R6/include -I/cgabriel/gtk/lib/glib/include -I/cgabriel/gtk/include
$ 

Se state utilizzando una variante della shell Bourne, come ad esempio bash, potete utilizzare l'apice inverso (non il semplice apice!) per eseguire gtk-config e sostituire il suo output. Un semplice Makefileper compilare Hello, World potrebbe essere questo:


CC=gcc

all: ciao.c
        $(CC) `gtk-config --libs` `gtk-config --cflags` -o hello hello.c

clean:
        /bin/rm -f *.o *~

Ovviamente questo Makefile è troppo semplice per le applicazioni vere. Il il capitolo Creare il proprio albero di sorgenti descrive un ambiente di compilazione reale utilizzando automake e autoconf.

gtk-config permette di localizzare GTK+ sul sistema dell'utente, invece di scrivere direttamente una posizione hard-coded all'interno del Makefile. Vi viene inoltre in aiuto se desiderate mantenere due diverse versioni di GTK+ sul vostro sistema. Se queste vengono posizionate in due locazioni dedicate, potete scegliere quale utilizzare modificando il percorso per raggiungere gtk-config.

Come funziona?

Questo semplice programma contiene gli elementi essenziali di una applicazione GTK+. Non contiene nessuna funzioni legata a Gnome. Ma dato che Gnome è basato su GTK+, vengono applicati gli stessi concetti.

Inizializzazione

Per prima cosa, GTK+ deve essere inizializzato:


  gtk_init(&argc, &argv);

Questa chiamata permette la connessione a un server X, e analizza alcuni argomenti da riga di comando noti a tutti gli applicativi GTK+. Gli argomenti analizzati vengono rimossi da argv, e argc viene di conseguenza decrementato. gtk_init() registra inoltre una funzione di "cleanup" utilizzando atexit(). In pratica, questo diventa importante unicamente quando eseguite un fork(): il processo figlio deve uscire utilizzando una chiamata a _exit() anziché a exit() per evitare di chiudere GTK+ nel processo padre.

Widget

Successivamente, qualunque programma avrà elementi di una interfaccia grafica. Nella tradizione di X Window, questi vengono chiamati widgets. Tutti i widget sono delle sottoclassi della classe base GtkWidget, in modo che possiate un GtkWidget * per riferirvi a questi. (Dato che il linguaggio C non fornisce un supporto nativo per gli oggetti, GTK+ fornisce il proprio metodo. Il il capitolo I tipi e gli oggetti in GTK+ descrive questo.)


  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  button = gtk_button_new();

  label  = gtk_label_new("Hello, World!");

  gtk_container_add(GTK_CONTAINER(button), label);
  gtk_container_add(GTK_CONTAINER(window), button);

  gtk_window_set_title(GTK_WINDOW(window), "Hello");
  gtk_container_set_border_width(GTK_CONTAINER(button), 10);


Ciascun widget fornisce una funzione chiamata gtk_nomedelwidget_new(), in modo analogo ai costruttori in C++ o Java. Questa funzione alloca un nuovo oggetto, lo inizializza, e ritorna un puntatore ad esso. Tutte le funzioni di tipo _new() restituiscono un GtkWidget *, anche se ne allocano una sottoclasse, unicamente per convenienza.

Una volta ottenuto un GtkWidget* rappresentante un oggetto, è possibile manipolarlo utilizzando i suoi metodi. Tutte le funzioni per i widget di GTK+ iniziano con il nome del tipo su cui operano, e accettano un puntatore a quel tipo come primo argomento. Nel codice mostrato sopra, gtk_container_add() accetta un GtkContainer* come primo argomento. La macro GTK_CONTAINER() esegue la conversione di tipo, il cast, per il GtkWidget*, ed esegue inoltre un controllo del tipo a runtime. Eseguire il cast è obbligatorio poichè il linguaggio C non interpreta automaticamente la relazione di ereditarietà.

Come potrete immaginare, GtkButton e GtkWindow sono emtrambe sottoclassi di GtkContainer. Un GtkContainer può contenere altri widget al suo interno. Il codice mostrato creare una finestre, posizione un pulsante al suo interno, e inserisce una etichetta di testo all'interno del pulsante. Quindi imposta il titolo della finestra, e aggiunge un piccolo bordo al pulsante (puramente estetico).

Segnali

Di conseguenza, vorrete gestire la cosa in modo da gestire le modifiche effettuate dall'utente al widget. In questa semplice applicazione ci sono due casi interessanti che possono avvenire: l'utente preme il pulsante, oppure chiude la finestra tramite il gestore di finestre. I widget (attualmente tutto i GtkObject) emettono dei segnali quando accade qualcosa di interessante, ai quali l'applicazione può rispondere. Per rispondere a un segnale, potete associarvi una funzione di callback. Ad esempio, registrare una funzione da chiamare quando il segnale viene emesso. Ecco ancora una volta il codice:


  gtk_signal_connect(GTK_OBJECT(window),
                     "delete_event",
                     GTK_SIGNAL_FUNC(delete_event_cb),
                     NULL);

  gtk_signal_connect(GTK_OBJECT(button),
                     "clicked",
                     GTK_SIGNAL_FUNC(button_click_cb),
                     label);


gtk_signal_connect() specifica l'oggetto GtkObject da monitorare, quale segnale attendere, la callback associata e infine un argomento user_data (un puntatore gpointer arbitrario che sarà passato alla funzione callback). La macro GTK_SIGNAL_FUNC() esegue il cast della callback verso una funzione standard. Dato che le funzioni callback possono avere una vasta varietà di tipi, l'alternativa sarebbe avere dozzine di varianti a gtk_signal_connect().

GTK+ esegue moltissimi e utilissimi controlli a runtime. La macro GTK_OBJECT() include un controllo di tipo a runtime in aggiunta al cast di C, mentre gtk_signal_connect() verifica che l'oggetto possa emettere il segnale specificato.

Entrare nel ciclo principale

Una volta preparato tutto, rimangono gli ultimi due passi: mostrare la finestre sullo schermo e attendere una risposta da parte dell'utente.


  gtk_widget_show_all(window);

  gtk_main();

  return 0;


gtk_widget_show_all() chiama ricorsivamente gtk_widget_show() su un contenitore e tutti i widget al suo interno. Le seguenti istruzioni sono, in questo caso, equivalenti:


  gtk_widget_show(label);
  gtk_widget_show(button);
  gtk_widget_show(window);

È necessario visualizzare ciascun widget che deve apparire sullo schermo. La funzione inversa è chiamata gtk_widget_hide(). I widget iniziano la loro vita nascosti, e possono essere nuovamente mostrati o nascosti un infinito numero di volte. È buona abitudine mostrare i widget figli prima del contenitore principale, altrimenti l'utente vedrà apparire per primo quest'ultimo, seguito dai figli. I widget non sono effettivamente visibili sullo schermo fino a che non viene mostrato il contenitore in cui si trovano. L'eccezione a questa regola è il GtkWindow, dato che non può avere widget "padri".

Una volta che i vostri widget sono stati visualizzati, dovete attendere che l'utente esegua una qualunque operazione su di essi. gtk_main() entra nel ciclo principale di GTK+. Questo ciclo è guidato dagli eventi (eventi-driven). Le azioni degli utenti generano degli eventi che di solito generano dei segnali che avvieranno le vostre funzioni di callback. gtk_main() gira all'infinito, attendendo che l'utente generi un evento. Il ciclo principale viene descritto dettagliatamente nel la sezione Il ciclo principale . Gli eventi e la loro relazioni con il ciclo principale sono descritti nella sezione 10.5.

Ancora su segnali e funzioni callback

Se uno dei segnali che il codice monitorizza viene emesso, la funzione di callback corrispondente viene chiamata. La nostra callback associata a "delete_event" termina il ciclo gtk_main() chiamando gtk_main_quit(). La callback per "clicked" sostituisce il testo dell'etichetta sul pulsante con il suo inverso. Da notare che l'etichetta è stata passata alla callback utilizzando il parametro user_data della funzione gtk_signal_connect().

Un errore comune è quello di credere che tutti i segnali utilizzino lo stesso tipo di funzione callback: non è vero. Ciascun segnale richiede una callback di un particolare tipo. Il segnale "clicked" utilizza un tipo di callback molto comune. Questa riceve un puntatore al widget che ha emesso il segnale e un qualunque user_data fornito dallo sviluppatore. Questa callback deve restituire void oppure otterrete un errore di corruzione di memoria.

"delete_event", invece, è un caso molto particolare. Questo accetta tre argomenti: il primo e l'ultimo sono analoghi al segnale "clicked", mentre il secondo è un puntatore all'evento che ha generato il segnale (un evento è un messaggio inviato dal server X alle applicazioni, come lo spostamento del mouse, la pressione di un tasto e così via). La callback per "delete_event" restituisce un valore "magico" : se questa restituisce FALSE, GTK+ distruggerà la finestra. Restituendo invece TRUE GTK+ non esegue nessuna operazione. Restituite TRUE se dovete eseguire una operazione diversa dalla distruzione delle finestra. Ad esempio, per notificare all'utente che esistono dei documenti non salvati all'interno dell'applicazione.

I file header dei widget sono la migliore guida di riferimento per le strutture delle funzioni callback. La struttura della classe del widget contiene uno spazio per il gestore di default dei segnali. Il vostro gestore deve essere modellato su quello di default. Ad esempio, nel file gtk/gtkbutton.h la struttura della classe di GtkButton è così composta:


struct _GtkButtonClass
{
  GtkBinClass        parent_class;

  void (* pressed)  (GtkButton *button);
  void (* released) (GtkButton *button);
  void (* clicked)  (GtkButton *button);
  void (* enter)    (GtkButton *button);
  void (* leave)    (GtkButton *button);
};

Il il capitolo I tipi e gli oggetti in GTK+ espone cosa sia esattamente una struttura di una classe. Per adesso, prestate attenzione unicamente ai puntatori a funzione e noterete che questi corrispondono a dei segnali. Per arrivare da questo:


  void (* clicked)  (GtkButton *button);

a questo:


  static void button_click_cb(GtkWidget* w, gpointer data);

aggiungete semplicemente alla callback gpointer data, oltre la struttura della classe. In Hello,World inoltre cambiato il tipo da GtkButton* a GtkWidget*. Questa è una operazione molto comune, dato che è più conveniente avere un GtkWidget*. L'argomento sarà sempre corrispondente al GtkButton che emette il segnale.

Ecco un altro utile esempio, il "delete_event" dal file gtk/gtkwidget.h:


  gint (* delete_event)            (GtkWidget          *widget,
                                    GdkEventAny        *event);

e la funzione di callback da Ciao, Mondo:


static gint delete_event_cb(GtkWidget* w, GdkEventAny* e, gpointer data);

Questo è quanto. Potete scrivere semplici applicativi GTK+ utilizzando unicamente le informazioni presenti in questa sezione. GTK+ e Gnome sono strumenti potenti per la creazione di applicazioni poiché potete tranquillamente pensare alle vere funzionalità dell'applicazione, invece di arrovellarvi per ottenere una finestra sullo schermo.


Copyright © 1995-1999 Apogeo srl, Milano