Looking for Computer Science  & Information Technology online courses ?
Check my new web site: https://www.yesik.it !

Dans cet article de la série Interface graphique avec SWT sous Jython, nous allons voir comment créer une interface SWT à partir de la console interactive de Jython.

Plus généralement, cette partie intéressera toute personne qui souhaite pouvoir accéder à une bibliothèque Java à partir d'un programme Python. En outre, vous aurez l'occasion de constater que l'utilisation du mode interactif de l'interpréteur Jython se révèle particulièrement pratique pour prendre en main ou tester une bibliothèque Java – sans avoir à passer par le cycle édite-compile-exécute.

Pour commencer

Avant toutes choses, je suppose que vous avez déjà installé Jython et SWT sur votre système.

Par contre, il se peut que SWT (ou plus précisément, le fichier swt.jar) ne soit pas dans votre CLASSPATH. Si vous êtes dans ce cas, il est important de l'y ajouter avant de lancer Jython. Ainsi, il saura où trouver les différentes classes dont nous allons avoir besoin.

Sous un system UNIX-like, et si vous utilisez le shell bash cela peut se faire avec la commande export):

sh$ export CLASSPATH=".:/usr/local/lib/swt/swt.jar"
sh$ jython
*sys-package-mgr*: processing new jar, '/usr/local/lib/swt-3.4-gtk-linux-x86/swt.jar'
Jython 2.5.0 (Release_2_5_0:6476, Jun 16 2009, 13:33:26) 
[Java HotSpot(TM) Client VM (Sun Microsystems Inc.)] on java1.6.0_12
Type "help", "copyright", "credits" or "license" for more information.
>>> 

A partir de maintenant, Jython saura trouver les classes de la bibliothèque SWT – ce que nous allons nous empresser de vérifier.

Mettez le bon chemin dans le CLASSPATH!

Comme vous le voyez dans la transcription ci-dessous, sur mon système, SWT est installé dans /usr/local/lib/swt. Si vous l'avez installé ailleurs il faut bien sûr ajuster le CLASSPATH en conséquence.

SWT en mode interactif

Jython permet l'accès transparent aux classes Java disponibles sur votre système. Ainsi, celles-ci seront vues et manipulées par le programmeur Jython exactement comme s'il s'agissait de classes Python.

Cet uniformité de traitement se retrouve même au niveau de l'importation des classes Java. En effet, là aussi Jython utilise la syntaxe de Python (avec la commande import ... – ou sa variante from ... import ...). Vous pourrez vous en rendre compte dans les lignes suivantes:

>>> from org.eclipse.swt.widgets import Display
>>> from org.eclipse.swt.widgets import Shell  
>>> display = Display()                        
>>> shell = Shell(display)     
>>> shell.open()

Et voilà, en quelques lignes, nous avons créé une fenêtre gérée par SWT. C'est bel et bien une bibliothèque Java que nous utilisons. Et c'est bel et bien une machine virtuelle Java qui fait tourner notre programme. Pourtant, le code est 100% Python.

N'hésitez pas à vous amuser avec cette fenêtre. Par exemple, vous pouvez changer certaines de ses propriétés, comme:

Par contre, la plupart du temps, les modifications ne seront visibles que quand la fenêtre sera redessinée. Vous aurez donc besoin de coder – et exécuter – la boucle événementielle:

>>> def run():
>>>     while not shell.disposed:            
...         if not display.readAndDispatch():
...             display.sleep()              
...     display.close()                      
...
>>> run()

Comme vous le voyez, j'ai encapsulé la boucle événementielle dans une fonction. C'est juste plus pratique à manipuler ainsi à partir de l'interpréteur interactif. Celle-ci est codée de telle manière qu'elle tourne jusqu'à ce que vous cliquiez sur la case de fermeture de la fenêtre.

Remarque:

Une particularité de SWT, c'est que les objets qui allouent des ressources système doivent être libérés explicitement lorsqu'ils ne sont plus utilisés. C'est le cas de l'objet display. C'est pourquoi je le ferme à la fin de la boucle événementielle:

>>>     display.dispose()

Ce qui veut aussi dire que si vous voulez relancer le programme, il faudra reprendre le code du début (c'est à dire, à partir du moment où l'on crée l'objet display)!

Plus!

Comme pour l'instant notre fenêtre est un peu vide, nous allons tout de même lui ajouter un widget. Un simple label, sous la forme d'une instance de la classe org.eclipse.swt.widgets.Label. La chose est des plus simples, et ne nécessite pas grande explication. Par contre, pour que la procédure soit bien claire, je reprends l'intégralité ce ce qu'il faut re-taper maintenant dans la console de Jython:

>>> display = Display()    # Créer un nouveau ``display puisqu'on a fermé le précédent
>>> shell = Shell(display) # Créer la fenêtre
>>>
>>> from org.eclipse.swt.widgets import Label
>>> from org.eclipse.swt import SWT
>>> label = Label(shell, SWT.CENTER)
>>> label.text = "Jython rules!"
>>>
>>> label.pack() # Redimentionne le widget pour s'adapter au texte
>>> shell.pack() # Redimentionne la fenêtre pour s'adapter à son contenu
>>> shell.open() # Affiche la fenêtre
>>> run()        # Lance la boucle événementielle

Remarque:

Les commandes ci-dessus supposent être saisies dans la même console Jython que le code de la partie précédente.

Si vous avez quitté Jython entre temps, il faudra penser à rajouter:

  • les imports pour org.eclipse.swt.widgets.Display et org.eclipse.swt.widgets.Shell
  • ainsi que la définition de la fonction run.

Comparaison

A titre de comparaison, voici le code complet de notre programme Python, suivi de son équivalent Java:

from org.eclipse.swt.widgets import Display
from org.eclipse.swt.widgets import Shell
from org.eclipse.swt.widgets import Label
from org.eclipse.swt import SWT
 
display = Display()
shell = Shell(display)
label = Label(shell, SWT.CENTER)
label.text = "Jython rules!"
label.pack()
shell.pack()
shell.open()
 
def run():
    while not shell.disposed:            
        if not display.readAndDispatch():
            display.sleep()              
    display.close()                      
 
run()
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Label;
 
public class HelloSWT {
        static Display      display;
        static Shell        shell;
 
        public static void run() {
                while(!shell.isDisposed()) {
                        if (!display.readAndDispatch())
                                display.sleep();
                }
                display.dispose();
        }
 
        public static void main(String[] args) {
                display = new Display();
                shell   = new Shell(display);
                Label label   = new Label(shell, SWT.CENTER);
                label.setText("Jython rules!");
                label.pack();
                shell.pack();
                shell.open();
 
                run();
        }
}

Beurk!

Oui, je sais: l'utilisation exclusive de membres statiques est ... ugly.

Mais c'est ce qui s'approche le plus des variables et fonctions globales de Python. Par ailleurs, une solution plus élégante aurait encore rajouté des lignes de code – et je ne voulais pas qu'on m'accuse de biaiser ce comparatif...

Bref, il est temps de faire les comptes:

Même si c'est un critère discutable, le code Java de ce simple exemple est quand même près de 50% plus long que le code Python! Et pourtant, ce dernier n'a absolument pas perdu en lisibilité à cause de sa forme plus compacte. On pourrait même dire que c'est le contraire...

Ainsi, même si le prix à payer est une certaine dégradation des performances, Jython réussit néanmoins son pari qui était d'apporter l'expressivité du langage Python sur la plate-forme Java.

Pour terminer, nous allons justement profiter de ce comparatif Python/Java pour examiner un des plus apportés par Jython pour améliorer la lisibilité des programmes: l'accès aux propriétés Java Bean.

Jython et Java Beans

Si vous comparez très attentivement les versions Python et Java du code précédent, deux lignes devraient attirer votre attention:

label.text = "Jython rules!"
# ...
 
while not shell.disposed:
# ...
label.setText("Jython rules!");
/* ... */
 
while(!shell.isDisposed()) {
/* ... */

Vous voyez que dans la version Jython, le code semble accéder aux propriétés text et disposed de l'objet shell. Alors que dans la version Java le code utilise les méthodes setText() et isDisposed().

En fait, Jython honore les conventions JavaBeans. Ainsi, lorsqu'on tente de modifier une propriété non-accessible, Jython regarde s'il n'y a pas une méthode setPropriété() accessible et qu'il pourrait utiliser à la place. Ainsi, quand on écrit:

label.text = "Jython rules!"

Comme label.text n'est pas une propriété accessible, Jython essaye d'utiliser à la place l'appel de méthode suivant:

label.setText("Jython rules!")

Note:

Je ne le fais pas dans ce programme d'exemple, mais, de la même manière, si on essayait de lire une propriété inaccessible:

print label.text

Jython substituerait – toujours conformément aux spécifications Java Bean – un appel au getter correspondant:

print label.getText()

Enfin, pour les propriétés dont la valeur est un booléen (comme shell.disposed), les spécifications Java Beans permettent de remplacer le getter par une méthode de la forme isPropriété(). Ce qui explique pourquoi:

while not shell.disposed:

Correspond à:

while (!shell.isDisposed()) {

Et la suite?

Voilà: notre premier tour d'horizon de l'utilisation de Jython pour créer une interface graphique avec SWT est terminé. N'hésitez pas à expérimenter: vous l'avez vu, la console interactive permet d'avoir des résultats quasi-immédiats. Une fois que vous en aurez assez, vous pourrez passer à la seconde partie de cet article pour voir comment associer une action à un widget.