La nostra prima applicazione
Dopo il Introduzione a PHP-GTK dedicato all'introduzione a PHP-GTK, è giunto il momento di capire come sia possibile creare delle applicazioni funzionanti. Come al solito,
il primo programma sarà molto semplice, ma utile per introdurre aspetti
fondamentali per la programmazione GTK. Il nostro obiettivo è quello
di creare un'applicazione come la seguente:
(scusate la qualità pessima dell'immagine, comunque dovrebbe rendere
l'idea)
Premendo sul pulsante 'Saluta' verrà visualizzato "Hello World"
nella Label modificabile. Premendo su 'Modifica' potremo visualizzare una
frase a notro piacimento, inserita all'interno dell'InputBox. Premendo 'Chiudi'
chiuderemo il tutto.
Utilizzeremo la libreria che abbiamo creato nell'Introduzione a PHP-GTK e che abbiamo chiamato gtk_application.lib. Il nostro primo programma, introdurrà all'utilizzo dei seguenti
widget:
- GtkWindow:
un widget che fornisce una finestra nella quale contenere i componenti principali
della nostra applicazione. Offre la possibilità di inserire un titolo,
scegliere i bordi e scegliere la sua posizione sullo schermo. Ovviamente
esistono altri metodi interessanti, che per ora non tratteremo. - GtkButton:
un widget di tipo container che emette un segnale quando vi si clicca sopra. - GtkLabel:
un widget che permette di visualizzare una piccola quantità di testo. - GtkEntry:
un widget che può contenere, in singola riga, del testo inserito
dall'utente. - GtkVSeparator,
GtkHSeparator:
widget che rappresentano una linea di separazione verticalo o orizzontale. - GtkHBox, GtkVBox,
GtkVButtonBox:
container che servono per visualizzare correttamente il layout delle nostre
applicazioni.
Creiamo un nuovo file, che chiameremo intro.php oppure intro.dgtk (in caso
abbiate deciso di utilizzare gli installer), ed all'interno vi scriviamo quanto
segue:
<?php
$myApp = require_once('gtk_application.lib');
//classes def
//callbacks
//main init
//widget placement
//signals
//main-loop
?>
Come potete notare, non abbiamo fatto nulla di particolare, solamente incluso
la libreria che abbiamo creato in precedenza ed assegnato l'istanza della
classe GtkApplication (restituita dal nostro include) alla variabile $myApp.
Per comodità, ho deciso di scrivere due classi da sfruttare in questa
applicazione. La prima è una semplice classe che ha la funzione di
creare la finestra principale, la seocnda permette di gestire un'elenco di
bottoni in modo relativamente semplice. Aggiungiamo il codice seguente sotto
il commento "//classes def":
class MainWindow extends GtkWindow{
function MainWindow($name, $title, $size, $border, $position){
$this->GtkWindow();
$this->set_name($name);
$this->set_title($title);
$this->set_usize($size[0], $size[1]);
$this->set_border_width($border);
$this->set_position($position);
}
}
class ButtonVList extends GtkVButtonBox{
function ButtonVList(){
$this->GtkVButtonBox();
$buttons = func_get_args();
foreach($buttons as $button){
$bt = &new GtkButton($button);
$this->pack_start($bt);
}
$this->show_all(); //mostro tutti i bottoni caricati
}
function connect_object_id($id){
$children = $this->children();
$bt = $children[$id];
if(!$bt)
return false;
$args = func_get_args();
call_user_method_array('connect_object', $bt, array_slice($args, 1));
}
}
Qui le cose si fanno leggermete più complicate di prima. La prima
classe è figlia della classe GtkWindow, che, come spiegato, serve a
visualizzare una finestra che conterrà la nostra applicazione. GtkWindow
ha vari metodi che servono per variare l'aspetto e lo stato della finestra
stessa; solitamente vengono modificati il titolo attraverso il metodo GtkWindow::set_title,
le dimensioni attraverso il metodo GtkWindow::set_usize
ereditato dalla classe GtkWidget, la dimensione dei bordi attraverso GtkWindow::set_border_width
ereditato dalla classe GtkContainer e la posizione della finestra al momento
del suo primo caricamento attraverso GtkWindows:set_position
. Tramite la classe MainWindow sarà quindi possibile creare una
finestra settando direttamente le sue proprietà dal costruttore pincipale.
La seconda classe permette di gestire una lista verticale di bottoni. La
classe eredita da GtkVButtonBox, che permette di visualizzare dei bottoni
verticalmente e correttamente posizionati. Il costruttore accetta una quantità
variabile di argomenti. Ognuno di questi rappresenta il titolo da associare
ad un bottone. Per ogni argomento passato, difatti, viene generato un bottone
che poi viene aggiunto al box. Queste due istruzioni, racchiudono 2 informazioni
molto importanti: la prima è che ogni classe Gtk* DEVE essere istanziata
per riferimento, anteponendo il simbolo & al costrutto "new";
la seconda è l'utilizzo dei Packer.
I packer sono un metodo tramite il quale le GUI gestiscono il posizionamento
ordinato dei widget all'interno di un container. Nel nostro caso abbaimo usato
GtkBox::pack_start
che permette di posizionare il widget passato nella parte più alta
o più a sinistra (in base al tipo di container, verticale o orizzontale)
del container non ancora occupata. Utilizzando più volte pack_start
in un container veticale, per esempio, incolonneremo i widget dall'alto verso
il basso. Vengono mantenuti correttamente eventuali bordi per il container
o spaziature standard da tenere tra i widget. Esiste un metodo simile, GtkBox::pack_end,
che inserisce i widget partendo o dal basso o da destra. I packer, ovviamente,
non si limitano a posizionare un widget, ma possono anche accettare parametri
aggiuntivi per controllare lo status del widget in caso di ridimensionamento
della finestra. Di questi tratteremo più avanti, ma potete controllare
la documentazione ufficiale per saperne di più.
La seconda classe, per permetterci di gestire i segnali notificati da ogni
bottone in modo corretto, dispone di un metodo, connect_object_id, che consente
di connettere una callback ad un segnale per un solo bottone. Basta specificare
come primo argomento l'indice del bottone all'interno della lista (basta seguire
l'ordine di inserimento: il bottone corrispondente alla prima stringa sarà
lo 0 e via dicendo ... ) e come argomenti successivi quelli che avremmo passato
normalmente al metodo connect_object.
Ora creiamo delle funzioni che sfrutteremo come callback da associare ai
segnali notificati dai bottoni. La callback da associare al pulsante chiudi
è già presente nella classe GtkApplication da noi sviluppata.
Aggiungete sotto il commento '//callbacks' le seguenti righe di codice:
function set_name($label, $entry){
$gettext = $entry->get_text();
$label->set_text($gettext);
}
function hello($label){
$label->set_text('Hello world');
}
La prima funzione, set_name, visualizza nella label modificabile la frase
da noi inserita nell box di input. Accetta 2 argomenti: il primo rappresenta
la label da modificare, ed è un'istanza di GtkLabel, il secondo rappresenta
il box contenente il testo ed è un'istanza di GtkEntry. La classe GtkLabel
dispone di vari metodi per la gestione del testo presente al suo interno.
In questo caso sfruttiamo GtkLabel::set_text,
che ci permette di modificare il testo contenuto nella label. Della classe
GtkEntry, invece, sfruttiamo il metodo GtkEntry::get_text
per prelevare il testo contenuto nel box e salvarlo in una variabile. Essendo
passati per riferimento, gli argomenti non saranno copiati ma faranno riferimento
sempre ai widget generati dalla nostra applicazione all'inizio. In questo
modo, tutte le modifiche a apportate agli argomenti, si riperquoteranno anche
sui widget principali.
La seconda funzione, che accetta come solo argomento l'istanza di GtkLabel,
sfrutta GtkLabel::set_text per modifcare il testo della label nell'ormai indispensabile,
per le prime applicazioni, "Hello world".
Terminata la preparazione, possiamo iniziare a creare i widget che comporranno
la nostra applicazione. Per creare un widget, ci basterà solamente
istaziare la classe che lo rappresenta. In questo modo il widget sarà
creato, e pronto per essere posizionato sullo schermo. Aggiungete, sotto al
commento '//main init', il seguente codice:
$window = &new MainWindow('main_win', 'Introduzione a PHP-GTK. by HTML.it',
array(400, 250), 5, GTK_WIN_POS_CENTER);
$box1 = &new GtkHBox();
$bbox = &new ButtonVList('Saluta', 'Modifica', 'Chiudi');
$separator1 = &new GtkvSeparator();
$box2 = &new GtkVBox();
$labelx = &new GtkLabel('Qui sotto c'è un'etichetta che verrà
modificata:');
$separator2 = &new GtkhSeparator();
$entry = &new GtkEntry();
$label = &new GtkLabel('');
Abbiamo creato, in successione:
- La finestra principale tramite la classe da noi creata, passandole come
argomento il nome della finestra, il titolo, le dimensioni iniziali, la
dimensione dei bordi e la posizione (sfruttando una costante built-in che
specifica che la posizione della finestra dovrà essere centrale rispetto
allo schermo); - Un box orizzontale necessario per contenere i tre widget principali che
comporranno la nostra applicazione: il box che conterrà i bottoni,
un separatore verticale, ed un box che conterrà le label e la entry; - La lista di bottoni;
- Un box verticale;
- Una label statica che non verrà modificata dal programma, a cui
viene passata come argomento la stringa da visualizzare; - Un separatore orizzontale
- Una entry dove inserire il testo a visualizzare;
- La label, inizialmente vuota, che visualizzerà il testo da noi
inserito.
L'operazione è stata abbastanza semplice, come avete potuto vedere.
Ora non ci resta che posizionare correttamente questi widget, associargli
eventuali callback e inizializzare il tutto. Andando per ordine, aggiungete
il seguente codice dopo il commento '//widget placement':
$window->add($box1);
$box1->show();
$bbox->set_layout(GTK_BUTTONBOX_SPREAD);
$box1->add($bbox);
$box1->show();
$box1->pack_start($separator1);
$separator1->show();
$box1->add($box2);
$box2->show();
$box2->pack_start($labelx);
$labelx->show();
$box2->pack_start($label);
$label->show();
$box2->pack_start($separator2);
$separator2->show();
$box2->pack_start($entry);
$entry->show();
$window->show_all();
Sfruttando i packer, posizioniamo i widget all'interno dei rispettivi container;
il metodo GtkContainer::add
ci permette di aggiungere un widget ad un container, posizionandolo in modo
simile a quanto avviene per pack_start. Se un widget non viene aggiunto o
posizionato, e non viene riciamato il metodo show, questi non viene visualizzato.
Il metodo GtkWidget::show
permette di visualizare il widget dal quale viene richiamato, mentre il metodo
GtkWidget::show_all
permette di visualizzare il widget dal quale è chiamato e tutti i suoi
widget figli. È buona norma, chiamare sempre il metodo show per ogni widget
non container, altrimenti show_all.
Una piccola precisazione va fatta per la funzione set_layout;
questa funzione, appartenete alla classe da noi creata ButtonVList, è
ereditata da GtkVButtonBox, e permette di scegliere il layout di visualizzazione
dei bottoni scegliendo una delle seguenti costanti come argomento:
- GTK_BUTTONBOX_DEFAULT_STYLE: lo stile di default, solitamente GTK_BUTTONBOX_EDGE;
- GTK_BUTTONBOX_SPREAD: specifica che i bottoni devono essere visualizzati
all'interno del box con spazi regolari ai bordi e tra uno e l'altro; - GTK_BUTTONBOX_EDGE: come sopra, ma senza spazi dal bordo superiore ed
inferiore; - GTK_BUTTONBOX_START: i bottoni verranno inseriti partendo all'alto o da
sinistra, visualizzati in base alla loro spaziatura interna e alla dimensione
dei bordi; - GTK_BUTTONBOX_END: come sopra, ma partendo dal basso o da destra.
In PHP-GTK troviamo molte costanti predefinite di indubbio utilizzo. Una
lista completa può essere tovata nella documentazione ufficiale.
Ora, per completare il tuto aggiungiamo dopo il commento '//signals' questo
codice:
$bbox->connect_object_id(0,'clicked', 'hello', $label);
$bbox->connect_object_id(1,'clicked', 'set_name', $label, $entry);
$bbox->connect_object_id(2,'clicked', $myApp->destroy());
e dopo il commento '//main-loop' quest'altro:
$myApp->set_main($window);
$myApp->main_loop();
Il primo spezzone di codice è quello fondamentale per il funzionamento
della nostra applicazione; senza di quello vedremmo il layout correttamente,
ma l'applicazione non funzionerebbe. Sfruttando il metodo connect_object_id,
associamo ai vari pulsanti presenti nella ButtonVList delle callback, da richiamare
in caso il pulsante sia cliccato: al pulsante 'Saluta' associamo la funzione
hello, al pulsante 'Modifica' la funzione set_name ed infine, al pulsante
'Chiudi' la funzione, presa dalla classe GtkApplication, che permette di terminare
la nostra applicazione GTK.
Il secondo spezzone di codice serve per settare la finestra principale, alla
quale la funzione set_main si preoccuperà di associare una callback
per chiudere GTK in caso la finestra venga chiusa, e per avviare il tutto.
Per facilitarvi nel testing dell'applicaizone, includo il codice sorgente
per intero:
<?php
$myApp = require_once('gtk_application.lib');
class MainWindow extends GtkWindow{
function MainWindow($name, $title, $size, $border, $position){
$this->GtkWindow();
$this->set_name($name);
$this->set_title($title);
$this->set_usize($size[0], $size[1]);
$this->set_border_width($border);
$this->set_position($position);
}
}
class ButtonVList extends GtkVButtonBox{
function ButtonVList(){
$this->GtkVButtonBox();
$buttons = func_get_args();
foreach($buttons as $button){
$bt = &new GtkButton($button);
$this->pack_start($bt);
}
$this->show_all();
}
function connect_object_id($id){
$children = $this->children();
$bt = $children[$id];
if(!$bt)
return false;
$args = func_get_args();
call_user_method_array('connect_object', $bt, array_slice($args, 1));
}
function &get_button($id){
return $this->buttons[$id];
}
}
function set_name($label, $entry){
$gettext = $entry->get_text();
$label->set_text($gettext);
}
function hello($label){
$label->set_text('Hello world');
}
$window = &new MainWindow('main_win', 'Introduzione a PHP-GTK. by HTML.it',
array(400, 250), 5, GTK_WIN_POS_CENTER);
$box1 = &new GtkHBox();
$bbox = &new ButtonVList('Saluta', 'Modifica', 'Chiudi');
$separator1 = &new GtkvSeparator();
$box2 = &new GtkVBox();
$labelx = &new GtkLabel('Qui sotto c'è un'etichetta che verrà
modificata:');
$separator2 = &new GtkhSeparator();
$entry = &new GtkEntry();
$label = &new GtkLabel('');
$window->add($box1);
$box1->show();
$bbox->set_layout(GTK_BUTTONBOX_SPREAD);
$box1->add($bbox);
$box1->show();
$box1->pack_start($separator1);
$separator1->show();
$box1->add($box2);
$box2->show();
$box2->pack_start($labelx);
$labelx->show();
$box2->pack_start($label);
$label->show();
$box2->pack_start($separator2);
$separator2->show();
$box2->pack_start($entry);
$entry->show();
$window->show_all();
$bbox->connect_object_id(0,'clicked', 'hello', $label);
$bbox->connect_object_id(1,'clicked', 'set_name', $label, $entry);
$bbox->connect_object_id(2,'clicked', $myApp->destroy());
$myApp->set_main($window);
$myApp->main_loop();
?>
Conclusioni
Il nostro tour introduttivo è terminato. Nei prossimi articoli tenteremo
di approfondire l'argomento PHP-GTK, imparando a sfruttare i widget più
complessi per creare applicazioni professionali e portabili. Le potenzialità
di GTK sono comunque talmente tante che l'unico modo per imparare ad utilizzare
la libreria al meglio è quello di sperimentare il più possibile.
Potrete ottenere aiuti sul forum di html.it, oppure iscrivervi alla mailing
list ufficiale.
Al prossimo articolo, in cui:
- Aplieremo la nostra libreria, aggiungendoclassi per la gestione dei menu;
- Impareremo a gestire le toolbar;
- Impareremo a gestire le finestre figlie;
- Impareremo a gestire le tabelle;
- Creeremo un'applicazion per decomprimere file compressi con PHP-GTK.