<- PW: Introduzione - Copertina - PW: Alle radici dello GNOME - Seconda Puntata -> |
PlutoWare
GTK+, il Gimp ToolKit, è nato come libreria per sviluppare Gimp (GNU Image Manipulation Program), ma col tempo si è trasformato in una libreria general purpouse, ed è stato adottato come base per un ambizioso progetto, la creazione di un desktop manager totalmente open source: Gnome. GTK+ è scritta in C, ma è fortemente orientata agli oggetti; infatti implementa l'idea delle classi, e dell'ereditarietà, implementa una infrastruttura "segnali/funzioni di callback" utilizzando i puntatori a funzione. Esistono, inoltre, varie versioni delle GTK+, sviluppate per essere utilizzate con i principali linguaggi di programmazione: ne esiste una versione per il C++, le GTK--, una versione per il pascal, per il perl, per python, per il PHP ed è stata sviluppata anche una versione per i sistemi win32 (per approfondimenti visitate il sito GTK+). Cominciamo ora a vedere i fondamentali di un programma GTK+, per poi proseguire il discorso nelle prossime puntate, approfondendo via via diversi argomenti, e giungendo ad essere in grado di costruire un'applicazione grafica completa basata su queste librerie, che offrono una flessibilità e una facilità d'uso con pochi eguali, unite ad un aspetto grafico davvero accattivante.
Ogni programma GTK+ deve includere l'header GTK+ è basato sulle librerie GDK e Glib. Glib sostituisce alcuni dei tipi fondamentali per garantire la portabilità su piattaforme diverse. Vi si trovano tipi gchar, gshort, glong, gint, gfloat, gdouble che non sono altro che i normali char, int,... Mentre gint8, guint8, gint16, guint16, gint32, guint32, gint64, guint64 sono tipi di dato con dimensione garantita, indipendenti dall'architettura sulla quale il programma verrà compilato. Su di un processore x86 o su un alpha, gint32 sarà sempre lungo 32 bit, mentre gconstpointer sarà sempre l'equivalente di const void*. Oltre ai tipi fondamentali sono implementate parecchie macro. Diverse funzioni della libreria standard del C sono state sostituite con altre più sicure o efficienti (g_malloc, g_free, g_print,...); inoltre sono disponibili funzioni e strutture dati per gestire liste singole e doppie, alberi, tabelle hash, ecc. La libreria GDK, invece, ha il compito principale di interfacciare GTK+ con le librerie di più basso livello che gestiscono X Window. Uno dei vantaggi che si ottengono adottando questo metodo è che, sostituendo solo GDK (le primitive grafiche) e facendo pochi cambiamenti a Glib, è possibile portare GTK+ su altri sistemi operativi (Windows ad esempio) con uno sforzo minimo. Di sicuro il modo migliore per imparare come funzionano queste librerie è quello di leggere del codice, quindi, come nelle migliori tradizioni inizieremo con il classico "Ciao Mondo". Come potete vedere dalle immagini in basso, questo programma crea una finestra con tre bottoni al suo interno. Consiglio di lanciare il programma da un emulatore di terminale e di osservare il comportamento della finestra e ciò che sarà stampato sullo schermo.
Una delle caratteristiche delle funzioni di GTK+ è quella di essere molto autoesplicative; provate a leggere il codice in basso, non dovrebbe essere molto oscuro! :) Per compilarlo utilizzate il comando:
Lo script di shell gtk-config si occuperà di fornire al compilatore ed al linker (semplificando, al GCC) nomi e percorsi delle librerie necessarie per una corretta generazione del file binario. Attenzione agli apici, bisogna usare gli apici inversi (`), ottenibili con la sequenza di tasti alt+apice, e non gli apici normali ('). #include <gtk/gtk.h> GtkWidget *window; GtkWidget *box; GtkWidget *bottone1, *bottone2, *bottone3; GtkWidget *etichetta; gint hide = 1; /* funzioni di callback: le funzioni di callback verranno richiamate quando si verificherà un qualche evento, click del mouse su un bottone,.. */ gint exit_cb(GtkWidget *w, gpointer data) { g_print("\nAddio mondo crudele... :(\n"); gtk_main_quit(); return (FALSE); } gint delete(GtkWidget *w, GdkEvent *event, gpointer data) { return TRUE; } void ciao_mondo(GtkWidget *w, gpointer data) { g_print("Siamo nella ciao mondo callback\n"); } void hide_show_mondo(GtkWidget *wg, gpointer data) { GtkWidget *bt = GTK_WIDGET(data); GtkWidget *etichetta; etichetta = gtk_object_get_data(GTK_OBJECT(wg), "etichetta"); if (hide == 0) { gtk_widget_show(bt); hide = 1; gtk_label_set(GTK_LABEL(etichetta), "Nascondi Mondo"); } else { gtk_widget_hide(bt); hide = 0; gtk_label_set(GTK_LABEL(etichetta), "Mostra Mondo"); } g_print("Siamo nella hide_show_mondo callback\n"); } int main(int argc, char *argv[]) { /* inizializzazione della libreria di GTK+ */ gtk_init(&argc, &argv); /* creazione della finestra principale */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* fisso le dimensioni della finestra a 250x200 pixel */ gtk_widget_set_usize(GTK_WIDGET(window), 250,200); /* diamo un titolo alla finestra */ gtk_window_set_title(GTK_WINDOW(window),"GTK Ciao Mondo test"); /* colleghiamo la finestra alle funzioni di callback */ gtk_signal_connect(GTK_OBJECT(window), /* oggetto che emette il segnale */ "delete_event", /* segnale emesso */ GTK_SIGNAL_FUNC(delete), /* funzione da richiamare */ NULL); /* dati da passare alla funzione chiamata */ gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(exit_cb), NULL); gtk_container_set_border_width(GTK_CONTAINER(window),15); /* creo una "scatola" che può contenere oggetti disposti in verticale, la inserisco nella finestra principale e la rendo visibile */ box = gtk_vbox_new(FALSE,1); gtk_container_add(GTK_CONTAINER(window), box); gtk_widget_show(box); /* creiamo tre bottoni */ bottone1 = gtk_button_new_with_label(" Ciao Mondo "); gtk_box_pack_end(GTK_BOX(box), bottone1, FALSE, FALSE, 15); gtk_signal_connect( GTK_OBJECT(bottone1), /* oggetto che emette il segnale */ "clicked", /* segnale emesso */ GTK_SIGNAL_FUNC(ciao_mondo), /* funzione da richiamare */ NULL); /* dati da passare alla funzione da chiamare */ gtk_widget_show(bottone1); bottone2 = gtk_button_new(); etichetta = gtk_label_new("Nascondi Mondo"); gtk_container_add(GTK_CONTAINER(bottone2),etichetta); gtk_object_set_data(GTK_OBJECT(bottone2), "etichetta", etichetta); gtk_widget_show(etichetta); gtk_box_pack_start(GTK_BOX(box), bottone2, FALSE, FALSE, 15); gtk_signal_connect( GTK_OBJECT(bottone2), "clicked", GTK_SIGNAL_FUNC(hide_show_mondo), (gpointer) bottone1); gtk_widget_show(bottone2); bottone3 = gtk_button_new_with_label(" Esci "); gtk_box_pack_start(GTK_BOX(box), bottone3, FALSE, FALSE, 15); gtk_signal_connect(GTK_OBJECT(bottone3), "clicked", GTK_SIGNAL_FUNC(exit_cb), NULL); gtk_widget_show(bottone3); gtk_widget_show(window); gtk_main(); return 0; } }
Esaminiamo il programma partendo dal classico metodo
la funzione gtk_init è molto importante: si occupa di inizializzare la
libreria GTK+, richiama glib_init e gdk_init (che a loro volta inizializzano GLib e GDK)
ed esamina la riga di comando; i comandi riconosciuti da GTK+ verranno eliminati
da argv e quello che rimane verrà passato al programma, che eventualmente
lo gestirà. Un programma grafico che utilizzi le librerie GTK+ deve
necessariamente richiamare questa funzione prima di ogni altra API di GTK+. Notate come GTK+ sia orientato agli oggetti. Tutte le funzioni che creano un nuovo widget avranno la seguente forma: gtk_NomeDelWidget_new(...). Queste funzioni allocano un nuovo oggetto e lo inizializzano, quindi ritornano un puntatore a quell'oggetto. Per poter usare esplicitamente il widget è necessario effettuare un cast al tipo voluto. Una volta ottenuto un GtkWidget*, è possibile manipolarlo utilizzando i suoi metodi; tutte le funzioni per i widget di GTK+ iniziano con il nome (minuscolo) del tipo su cui operano e accettano un puntatore a quel tipo come primo argomento. Il prototipo della funzione che inserisce un qualsiasi oggetto in un widget contenitore è il seguente: gtk_container_add(GtkContainer *container, GtkWidget *wiget);essa accetta un GtkContainer* come primo argomento, un GtkWidget* come secondo. GtkContainer è un oggetto derivato da GtkWidget; eredita quindi tutte le sue proprietà e metodi. La macro GTK_CONTAINER() esegue un cast, una conversione di tipo da GtkWidget* a GtkContainer*. Eseguire il cast è obbligatorio poichè il linguaggio C, non essendo un linguaggio ad oggetti, non interpreta automaticamente la relazione di ereditarietà. GTK+ è "guidato dagli eventi", cioè il nostro programma se ne starà per la maggior parte del suo tempo a non fare niente, ad attendere che avvenga qualcosa, e quando questo qualcosa accade, passerà il controllo ad una apposita funzione. Questo meccanismo si realizza con la tecnica dei segnali. Nel caso del nostro programma, quando si clicca su di un bottone, questo emetterà un certo segnale (o alcuni di essi); noi non dovremo far altro che intercettare quello che ci interessa e chiamare la funzione appropriata. Tutto ciò avviene utilizzando la seguente API:
Ecco un piccolissimo elenco degli eventi che possono essere intercettati:
Notate che la funzione di callback delete() ritorna TRUE; ritornando questo valore semplicemente informiamo il window manager di voler continuare la nostra applicazione; ritornando il valore FALSE, al contrario, il wm emetterà il segnale "destroy" che chiuderà il nostro programma. Detto ciò una prima applicazione che potrebbe essere realizzata è una finestra di dialogo che chieda all'utente se voglia effettivamente uscire o no, in caso di risposta affermativa si ritornerà FALSE e il programma verrà terminato, altrimenti si ritornerà TRUE e si continuerà normalmente.
box = gtk_vbox_new(FALSE,1); gtk_widget_show(box); bottone1 = gtk_button_new_with_label(" Ciao Mondo "); gtk_box_pack_end(GTK_BOX(box), bottone1, FALSE, FALSE, 15); gtk_widget_show(bottone1);la prima riga crea una scatola verticale, la seconda rende visibile la scatola, la terza crea un bottone con l'etichetta "Ciao Mondo", la quarta inserisce il bottone nella scatola e l'ultima rende visibile il bottone. In GTK+ le "scatole" sono molto importanti; sono dei widget invisibili atti a contenere altri widget (un po' come le tabelle in HTML, che possono contenere altre tabelle, che a loro volta sono usate per disporre testo o grafica). Esistono due tipi di scatole, le vbox (le scatole verticali) e le hbox (le scatole orizzontali). Componendo queste due scatole, l'unico limite alla disposizione degli oggetti nelle finestre sarà imposto dalla vostra fantasia. Ad es. possiamo avere una scatola orizzontale che ne contiene altre tre, la prima delle quali sarà una scatola verticale nella quale inseriremo dei bottoni, la seconda scatola orizzontale conterrà del testo, la terza altri bottoni
la differenza fra una vbox e una hbox si può notare osservando la zona contornata dal rosso e quella contornata dal verde. Entrambe le scatole contengono dei bottoni, ma la prima, essendo una vbox li ha disposti in verticale (rosso), la seconda, essendo una hboxi, li ha disposti in orizzontale. In conclusione, gli oggetti contenuti (bottoni) assumono una diversa disposizione in funzione della natura del contenitore.
I prototipi per la creazione di una scatola e l'impacchettamento di oggetti nella
scatola sono i seguenti: GtkWidget *gtk_hbox_new(gboolean homogeneus, gint spacing); GtkWidget *gtk_box_pack_start(GtkWidget *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding); In gtk_hbox_new (o gtk_vbox_new) homogeneus stabilisce se gli oggetti contenuti nella scatola devono avere tutti la stessa dimensione (orizzontale se hbox, verticale se vbox); se il flag è settato a TRUE, allora tutti gli oggetti avranno la medesima dimensione di quello di dimensione massima. Ad esempio, supponiamo di avere due bottoni orizzontali, il primo con etichetta "OK" e il secondo con etichetta "precipitevolissimevolmente"; se homogeneus è settato a TRUE, allora il bottone con "OK" sarà lungo quanto quello con "precipitevolissimevolmente", mentre se homogeneus è FALSE i bottoni occuperanno lo spazio minimo che è loro necessario.
Se homogeneus è TRUE il valore expand della funzione
In
In GTK+ è anche presente l'oggetto table, che realizza un po' quello che è una tabella HTML, o, secondo quanto visto finora, un vettore di hbox in una vbox (o di vbox in un hbox, spero abbiate capito... ;). Le API, una volta familiarizzato con le box, sono molto intuitive, per cui non scenderemo nel dettaglio.
Proseguendo con il programma si incontra
L'ultima funzione da esaminare per quel che riguarda i widget presenti in
questo programma è Il primo argomento della funzione di callback è un puntatore al widget che emette il segnale che la farà richiamare. Questo puntatore è usato per cambiare l'etichetta sul bottone.
Infine, occorre richiamare la funzione In questo breve programma si è fatta conoscenza con il sistema ad oggetti implementato da GTK+. Per chi fosse interessato e avesse voglia di approfondire l'argomento vi rimando al prossimo articolo, nel quale sarà presentata l'implementazione degli oggetti in GTK+. Siete invitati inoltre a confrontare quanto detto finora coi documenti contenenti le API Reference di GTK+, GLib e GDK; essi sono una fonte preziosa di informazioni, e una volta familiarizzato coi concetti di base dei widget, è relativamente semplice consultarli per ricavare da soli come far sì che quel widget che proprio non ne vuole sapere di fare quello che dico io... :) |
Sono iscritto in Ingegneria Informatica al Federico II di Napoli. I miei ultimi interessi informatici sono rivolti all'xml e particolarmente a GConf. Attualmente sto scrivendo una rubrica per GTK+ e GNOME, naturalmente rilasciata con licenza GPL. Nel tempo libero mi piace programmare, leggere (Stephen King è il mio autore preferito), stare con la mia ragazza e sentire i miei amici. |
<- PW: Introduzione - Copertina - PW: Alle radici dello GNOME - Seconda Puntata -> |