Nous avons vue dans la première partie de cet article comment installer GTK sous Windows et Unix. Il est temps maintenant de se lancer dans le développement au travers d'un exemple.

Si vous n'avez pas encore installé PHP-GTK, je vous encourage donc à lire (ou relire) le premier article consacré à ce sujet.

A vrai dire, sortir d'une approche Web traditionnelle n'a pas été simple pour moi. Surtout lorsque l'on nage dedans depuis des années. Il me fallait :

• oublier l'approche client (navigateur) et serveur (apache),
• fouiller dans ma mémoire afin de retrouver des reflexes de développement en programmation evenementielle (mes développements en C et X11/Motif sous Alpha/OpenVMS ou encore XView sous Sparc/SunOS sont décidement bien lointains),
• trouver un exemple suffisament simple et didactique.

J'ai donc opté pour développer un chronomètre en PHP GTK. Ou plutôt...un Dirty Chronomètre, dans la mesure ou le code est largement perfectible. Je n'ai pas cherché à l'optimiser car là n'était pas le but. Je voulais avant tout montrer comment utiliser GTK. Et puis, en fin de compte, il tourne plutôt bien :)

The Dirty Chronomètre

L'idée est simple. Développer un chronomètre en PHP-GTK disposant des fonctionnalités suivantes :

• start : démarre le chrono,
• stop : arrete le chrono,
• reset : ré-initialise le chrono,
• quit : sort (proprement :p) de l'application.

Description de l'interface

Un développement en PHP-GTK débutera par 2 choses :

• le chargement dynamique de la DLL php_gtk (.so sous Unix et .dll sous Windows),
• la construction de l'interface.
L'interface de notre Dirty Chronomètre comprendra :

• une fenêtre principale,
• 4 boutons (correspondants aux 4 actions décritent précédement),
• 1 zone d'affichage de l'état du chronomètre et du temps.

Pour des raisons qui nous comprendrons plus tard, j'ai opté pour construire un tableau décrivant les différents boutons (nom et fonction associée). J'en profite aussi pour initialiser l'état du chronomètre au démarrage de l'application :

// Temps initial
$time_start="00:00:00";
// Liste des boutons et actions associées
 $options = array(
	'start' => 'chrono_start',
	'stop' => 'chrono_stop',
    'reset' => 'chrono_reset',
	'quit' => 'chrono_quit'
);
Codage de l'interface

Afin de construire tout cela (j'ignore si je l'ai fait dans les règles de l'art, les guruzzz de GTK m'en excuserons je l'espère), j'ai commencé par créer la fenêtre principale (GtkWindows). A l'interieur de celle-ci, j'ai placé une première boite (GtKBox). Cette boite m'a alors servi pour en accueillir 2 autres : l'une pour y placer les boutons (GtkButton) et l'autre pour y placer la zone d'affichage (GtkFrame et GtkLabel).

En examinant plus finement le code, vous remarquerez que :

• la construction d'une interface à l'aide d'objets Gtk se fait comme dans un jeu de construction. Les éléments (widgets) s'empilent les uns au dessus des autres avec des dépendances. A ce titre, les widgets GtkFrame et GtkLabel dépendent d'une GtKBox, qui dépend elle même d'une autre GtKBox, qui depend à son tour de la GtkWindows principale,
• l'ensemble est bufferisé avant d'être affiché,
• chaque widget acceptent de nombreuses options (taille, mise en forme, etc.).

Voici le code :

// Chargement de la dll
 
if (!class_exists('gtk')) {
	if (strtoupper(substr(PHP_OS, 0,3) == 'WIN'))
		dl('php_gtk.dll');
	else
  		dl('php_gtk.so');
}

// Création de la fenêtre
 
$window = &new GtkWindow();
$window->set_name('Network Control');
$window->set_usize(200, 100);
$window->set_uposition(20, 20);
$window->connect('destroy', 'shutdown');
 
// Création d'une première boite globale
 
$box1 = &new GtKVBox();
$box1->set_border_width(0);
 
$window->add($box1);

// Création d'une seconde boite : boutons

$box2 = &new GtKHBox();
$box2->set_border_width(5);
$box2->set_usize(200,40);
 
$box1->pack_start($box2, false);

// Création des boutons
 
while(list($k,$v)=each($options)) {
	$button = &new GtkButton($k);
	$button->connect('clicked', $v);
	$box2->pack_start($button);
}
 
// Création du troisième boite : frame/label
 
$box3 = &new GtKVBox();
$box3->set_border_width(5);
$box3->set_usize(200,60);
 
$box1->pack_start($box3, false);
 
// Création de la frame/label

$frame = &new GtkFrame('Dirty Chronometre');
$label = &new GtkLabel("00:00:00");
$label->set_justify(GTK_JUSTIFY_CENTER);
$frame->add($label);
$label->show();
$box3->pack_start($frame);
$frame->show();

// On affiche tout
 
$window->show_all();
Gtk::main();
La gestion des boutons : Callback

Comme vous l'avez vu précédement, un tableau permet de décrire les différents boutons (nom et fonction associée). En qualité de bouton, nous allons pouvoir gérer des évenements. Dans le cas présent, il va sagir d'évenements de type clicked correspondant à un clique de souris. Tout se joue dans la section de code déjà présentée plus haut. Mais il n'est pas inutile de la revoir :

while(list($k,$v)=each($options)) {
	$button = &new GtkButton($k);
	$button->connect('clicked', $v);
	$box2->pack_start($button);
}
Sachant que pour rappel, notre tableau ressemblait à ceci :

// Liste des boutons et actions associées
 $options = array(
	'start' => 'chrono_start',
	'stop' => 'chrono_stop',
    'reset' => 'chrono_reset',
	'quit' => 'chrono_quit'
);
En clair, un clique sur le bouton start va générer un appel (on parle de callback) vers la fonction chrono_start. Tout simplement :)

Les fonctions

Il nous reste donc à coder l'ensemble des fonctions dont nous aurons besoin. Certaines sont directement liées à un boutons ou à GTK, d'autres ne le sont pas. Vous noterez l'utilisation des variables globales permettant de pointer vers les différents widgets gérés.

<?php
// Microtime

function getmicrotime() {
    list($usec, $sec) = explode(" ",microtime());
    return ((float)$usec + (float)$sec);
} 

// Affichage du chrono
 
function refresh () {
    global $label, $time_start;

    $time_now = getmicrotime();
    $tmp = $time_now-$time_start;
    $tmp=explode(",",$tmp);

    $seconde=$tmp[0];
    $dixieme=substr($tmp[1],0,2);

    if($seconde>=60) {
        $tmp=$seconde;
        $minute=(int)($seconde/60);
        $seconde=$tmp-($minute*60);
    }
    else
        $minute=0;

    if($seconde<10)
        $seconde="0".$seconde;

     if($minute<10)
        $minute="0".$minute;
      
    $time=$minute.":".$seconde.":".$dixieme;

    $label->set_text("$time");
    return 1;
}

// Action du bouton 'start'
 
function chrono_start() {
    global $frame, $id, $time_start;

	if($time_start=="00:00:00")
	    $time_start=getmicrotime();
    $frame->set_label("Dirty Chronometre");
    $id=Gtk::timeout_add (1, refresh);
}

// Action du bouton 'stop'
 
function chrono_stop() {
    global $frame, $id;

    $frame->set_label("Dirty Chronometre is stop");
    Gtk::timeout_remove($id);
}

// Action du bouton 'reset'

function chrono_reset() {
    global $frame, $label, $id, $time_start;

	$time_start="00:00:00";
	$time=$time_start;

	if($id)
	    Gtk::timeout_remove($id);
    $frame->set_label("Dirty Chronometre is reset");
	$label->set_text("00:00:00");
}

// Action du bouton 'quit'
 
function chrono_quit() {
	print "GTK PHP Rulezzz.\n" ;
	Gtk::main_quit();
}
Voila, nous dispons de l'ensemble des fonctions de notre chronomètre.

Et les temporisations ?

Et oui, j'ai oublié de vous parler d'un détail très important dans le cas de notre développement : les temporisations !

Ah, j'adore les temporisations ! Surtout lorsque je les implémentais un assembleur :)

En gros, une temporisation permet de demander à GTK d'exécuter quelque chose même s'il ne se passe rien. Et dans le cas de notre chronomètre ce sera fort utile dans la mesure ou le temps doit s'écouler suite à un clique sur le bouton start. Et même si l'utilisateur reste inactif. Bref, ca doit tourner !

Ceci est possible grace Gtk::timeout_add(). Il suffit de préciser une temporisation (en millisecondes) et une fonction vers laquelle il faudra pointer. Et le tour est joué. Voir plus précisement le code de la fonction chrono_start().

Il est évidement possible de tuer une temporisation en utilisant une fonction connexe : Gtk::timeout_remove(). Voir plus précisement le code de la fonction chrono_stop().

Conclusion : ca tourne !

Voila ! Notre chronomètre est operationnel. Et il tourne !

Bon, tout ceci vous semble peut-être encore un peu brumeux. Mais je vous engage à reprendre tout cela à tête reposée et à vous plongez dans la découverte de PHP-GTK. C'est vaste, mais pas si compliqué que cela. Quant aux perspectives...elles sont nombreuses !

A quand un phpMyAdmin en PHP-GTK ? Humm ? :)

Allez, bon courage. En esperant vous avoir donné envie d'aller plus loin.

PHP-GTK (Part I) : Installation
PHP-GTK Home
PHP-GTK Download
PHP-GTK Manuel
PHP-GTK FAQ
GTK API
Tutorial (en francais)