Flux RSS d'opikanoba.org

Sur l’interopérabilité, l’architecture et les systèmes d’informations de santé


Billets de la catégorie ‘python’

Jython : Java et Python

JPython est né d’une volonté d’utiliser les technologies Java avec un langage simple et puissant : Python. L’interpréteur python a donc été réécrit en Java pour permettre le mélange entre les classes du jdk et l’intrépeteur python au sein de la même machine virtuelle Java. Suite à des problèmes de license déténue par le CNRI (qui a vu la naissance de Python et de JPython), un nouveau nom a été donné à ce projet : Jython.

jython
Jython 2 est une version plus mature, qui subit un processus de développement plus intense que celui de JPython.

Jython permet de :

  • compiler dynamiquement du python en bytecode Java
  • hériter des classes Java en Jython
  • compiler statiquement (création d’applets, servlets, beans…)
  • utiliser la syntaxe et les modules python dans les programmes Java
  • avoir un interpréteur python manipulant des objets Java
  • Jython est certifié 100% Pure Java (portabilité assurée)

La procédure d’installation de Jython 2 n’a plus rien à voir avec celle de son ancêtre JPython 1.1. Elle est en effet vraiment trés simple. Pour ceux qui désirent travailler avec JPython 1.1, il faut non seulement installer JPython mais aussi l’errata de Finn Bock permettant de fixer pas mal de petites défaillances (voir liens). Cela dit, il n’y a pas vraiment de raison d’utiliser cette dernière, d’autant plus que la compatibilité ascendante est assurée.

Expérimentation de Jython

Voici un programme simple qui, décliné en plusieurs étapes permet de parcourir les possibilités d’utilisation du couple java/python. L’objet du programme est de retrouver des fichiers dans une arborescence, en utilisant les expressions régulières pour spécifier les noms de fichier.

 Le moteur de recherche de ces fichiers est écrit en python, et cela pour :

  • la faciliter d’écriture
  • bénéficier de la puissance des expressions régulières, module fournit dans la distribution python

Par exemple, pour trouver les fichiers python contenant dans le nom ‘mllib’ et étant suffixés par ‘py’ ou ‘pyc’ contenus dans le répertoire lib de la distribution python, en utilisant l’interpréteur python :

Python 2.0 (#2, Oct 30 2000, 14:29:42)
[GCC 2.8.1] on sunos5
Type “copyright”, “credits” or “license” for more information.
>>> import pyFind
>>> finder=pyFind.FileFind()
>>> finder.setPath(”/users/public_w3p/share/python/Python-2.0/lib”)
>>> finder.setExprFile(”[/].*?mllib[.](py|pyc)$”)
>>> finder.process()
>>> for elem in finder.getFoundFiles():
…     print elem
…
/users/public_w3p/share/python/Python-2.0/lib/python2.0/htmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/sgmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/xmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/htmllib.pyc
/users/public_w3p/share/python/Python-2.0/lib/python2.0/sgmllib.pyc
/users/public_w3p/share/python/Python-2.0/lib/python2.0/xmllib.pyc
/users/public_w3p/share/python/Python-2.0/lib/python2.0/test/test_xmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/test/test_xmllib.pyc
>>>

Une couche graphique

Pour rendre le moteur de recherche disponible de façon plus conviviale, une interface graphique est rajoutée. Pour cela, l’utilisation de Jython permet d’utiliser

  • un composant python existant
  • les librairies Java/Swing existantes

Jython sert alors de liant entre ces différentes technologies.

interface de FindGui

En plus de la présentation graphique, on peut rajouter la possibilité d’exécuter une action en double-cliquant sur un élément de la ul (classiquement une action d’édition ou de suppression).

Il y a trois (au moins) façons de réaliser ce programme. Les trois étapes décrites par la suite représentent une évolution du même programme depuis une approche 100% Java jusqu’à l’approche 100% Jython.

Pour chaque étape, le composant de recherche python décrit plus haut est utilisé, et :

  1. 100% du GUI en Java
  2. les composants graphiques adaptés de swing en Java, le modèle de données en Java, l’application graphique en Jython
  3. 100% du GUI en Jython

Etape 1 : Java utilise Python

Cet exemple illustre l’utilisation de l’interpréteur python au travers du code Java. Le but ici est d’utiliser le composant de recherche écrit en python dans du code Java. Le programme étant assez simple, voici un diagramme de classe :

Etape 1

Le package dont le rôle de contrôleur devrait faire l’interface entre les données, le graphique et l’interpréteur python, a été volontairement laissé de côté afin de réaliser un exemple court et fonctionnel. Il est à compléter.

Main
La classe Main représente juste le point de départ de l’application
FindGui
Le package contient la classe de l’interface graphique: une JFrame spécialisée. Elle recoit les évenements provenant de l’utilisateur, pilote l’interpréteur python, remplit le modèle de données, exécute ce qui doit l’être lors d’un double-clique (en somme GUI + contrôleur).
FindData
Le package contient la classe gérant les données issues de la recherche.
SwingAdapter
Le package contient les adaptations faites d’après Swing pour obtenir le rendu voulu. En particulier, la gestion du double-clique et la façon d’afficher chaque élément de la liste (en deux couleurs). Dans les autres exemples, tous ces services sont réalisés par du code Jython en utilisant la dérivation d’objets Java.

Le diagramme suivant représente l’organisation en terme de classes :

find class

Utilisation de l’interpréteur

Pour utiliser le composant de recherche python défini dans le fichier (module) pyFind.py, et plus précisement la classe FileFind, il faut récupérer une instance de celle-ci.

Ceci se réalise par plusieurs étapes simples :

importation du module
La classe org.python.core.imp est utilisée pour réaliser l’importation du modulePyObject
findMod = imp.importName("pyFind", true);
création d’une instance
Une instance est créée (par réflexion) de la classe FileFind. Elle est représentée par un PyObject (org.python.core). C’est sur cette instance qu’il faut invoquer des méthodes.
PyObject findClass = findMod.__getattr__("FileFind");
findInstance = findClass.__call__();
passage de paramètres, appel de méthodes
Il suffit d’invoquer une méthode (premier argument) sur l’instance précédemment créée en passant les bons paramètres. Les types de base sont définis dans le package org.python.core. Une chaine de caractères passée comme paramètre est un PyString. Un entier est un PyInteger, un entier long un PyLong, etc…
PyObject res;
// def setPath(self,p):
res = findInstance.invoke("setPath",new PyString(whereText.getText()));
// def setExprFile(self,r,simplified)
res = findInstance.invoke("setExprFile",new PyString(whatText.getText()));
// def process(self):
res = findInstance.invoke("process");
récupération du résultat
Pour retrouver les différentes valeurs résultant de la recherche et stocker dans un tableau python, il suffit d’itérer sur cette liste et les insérer dans le modèle de données Java. Chaque élément est récupéré dans un PyObject, qui fournit la méthode toString.
PyObject item;
for (int i = 0; (item = res.__finditem__(i)) != null; i++) {
dataModel.addElement(item.toString());
}

Distribution

Les fichiers sont regroupés dans des archives, format zip et tarball. Les sources sont sous licence GPL. Le makefile fournit permet de compiler le code java et de lancer l’execution. Les possibilites sont compile,clean, exec. Le ‘make’ sans argument permet de tout enchainer.

make compile
make exec
ou
make

Téléchargement :

Etape 2 : 50/50 Java/Jython

Cette étape permet d’écrire l’application graphique en Jython et non plus en Java. Il s’agit ici de modifier la classe FindGui sans pour autant changer tous les packages.

Le but est ainsi de démontrer qu’il est possible de créér une classe Python dérivant de classe(s) Java, soit par extension, soit par implémentation. Cette classe Python utilise alors des classes Java.

Modele de Find

Migration vers Jython

La syntaxe Python étant plus simple et non typée, le code s’en trouve allégé (un des nombreux avantages du langage Python). Les principales différences sont évoquées ci dessous.

Le déclaration de la fenêtre principale devient :

class FindGui(JFrame, ActionListener):

à la place de :

public class FindGui extends JFrame
          implements ActionListener

La déclaration du constructeur (nommé __init__)

def __init__(self,title):

à la place de :

public FindGui(String title) {

A chaque déclaration d’une méthode Python, il faut passer comme premier argument l’objet lui même, celui ci est représenté par “self” (équivalent au “this” Java). Pour appeler le constructeur de la super classe, il faut explicitement le nommer comme n’importe quel autre appel :

JFrame.__init__(self,title, visible=1)

à la place de :

super(title);

En python, et à fortiori en Jython, les variables membres n’ont pas besoin d’être déclarées. Néanmoins, il est possible de le faire, tout dépend de la façon de programmer.

Les variables membres, au même titre que les méthodes, sont stockées dans un dictionnaire interne de la classe. Donc à chaque fois que l’on fait référence à un self.toto, si dans le dictionnaire, la clé ‘toto’ n’existe pas et qu’il s’agit d’une affectation, une nouvelle clé est insérée ainsi que la valeur associée. Si la valeur n’existe pas dans le dictionnaire et que l’on veut un accès en consultation, une exception est levée.

En prenant deux variables membres au hasard, l’une est déclarée dans le constructeur et affecté à rien (None), l’autre n’est déclarée que lors de l’instanciation du JButton.

self.foundlist = None
self.searchButton = swing.JButton("Recherche")

En Java, il y a d’abord la déclaration, puis l’instanciation (bien que l’on puisse faire les temps en une seule étape) :

// déclaration private JList foundlist;
private JButton searchButton;
// création des objets
foundlist = new JList(dataModel);
searchButton = new JButton("Recherche");

Distribution

Les fichiers sont regroupés dans des archives au format tarball ou zip. Les sources sont sous licence GPL.
Le makefile fournit permet de compiler le code java, de créer un jar, et de lancer l’exécution. Les possibilités sont compile,jar,clean, et exec. Le ‘make’ sans argument permet de tout enchaîner.

Dans cette version, comme il y a du java à compiler, il faut mettre le bytecode dans un fichier jar pour que l’interpreteur jpython puisse trouver les classes.

make compile
make jar
make exec

ou

make

Téléchargement :

Etape 3 : Jython pilote java

Cette version est certainement une des plus intéresssantes, puisque l’ensemble du code écrit est du code Jython. Cela signifie que Jython est utilisé comme glue. Il permet de faire fonctionner les composants Java et/ou Python ensemble sans pour cela avoir besoin de créer un seul fichier à compiler. D’ailleurs, dans les sources, il n’existe plus de Makefile, seul l’interpréteur Jython est nécessaire.

Distribution

Les fichiers sont regroupés dans des archives au format tarball ou zip. Les sources sont sous licence GPL.
Pour lancer l’interface, il suffit de faire :

  cd step3
  jpython findgui.py

Téléchargement :

Python et les listes

S’il y a bien un concept sur lequel Python est éclatant, c’est bien le support natif des listes. Les listes, les fonctions lambda, filter et la forme associée au concept de liste “[expression for e in list]”. La méthode de collecte de données utilisée dans l’exemple utilise ces concepts, rendant le code plus concis et plus facile à comprendre. Voici un petit rappel pour clarifier les choses. Une fonction lambda est une fonction anonyme, qui associée à map ou filter (fonctions du langage Python) permet d’appliquer un traitement identique sur chaque élément d’une liste, et ce en une seule ligne. Par exemple, multiplier tous les éléments d’une liste d’entiers par 2 ou ne garder que les nombres pairs :

>>> liste=[1,2,3,4,5,6]
>>> map(lambda e: e*2, liste)
[2, 4, 6, 8, 10, 12]
>>> filter(lambda e: e % 2 ==0, liste)
[2, 4, 6]

L’inconvénient des fonctions lambda reste son écriture, qui rappelle les langages fonctionnels comme prolog ou caml. Cette écriture qui contraste fortement avec la programmation classique déroute souvent les programmeurs. Une autre forme peut alors être utilisée “[expression for e in liste]”. D’aucuns prétendent qu’elle est plus claire… Chacun se forgera une opinion.

>>> [e*2 for e in liste]
[2, 4, 6, 8, 10, 12]

Statistiques en SVG avec Python

XML a apporté son lot de nouvelles applications. SVG est l’une d’entre elles. SVG permet de faire du dessin vectoriel de façon très simple et sans librairie particulière puisqu’il s’agit d’un format XML. Nous allons voir comment utiliser la puissance de python pour collecter des informations sur la taille des répertoires d’un disque et générer un graphique à base d’histogrammes représentant les résultats.
(Login: n°121, novembre 2004 - Frédéric Laurent)

La figure 1 offre un aperçu du diagramme SVG d’occupation du disque que nous obtiendrons avec notre court exemple Python. Pour un répertoire et une profondeur donnés (ici le répertoire c:\Python et une profondeur 2), le graphique permettra de comprendre quels sont les répartitions en termes de pourcentage des différents répertoires contenus dans le chemin initial.

[fichier SVG]

statistiques
Figure 1: Statistiques de l’occupation de la
distribution python

Pour construire un tel graphique, le programme se divise en deux parties. La première permet d’explorer le système de fichiers et de collecter les informations de tailles des répertoires. La seconde partie met en forme ces données. Pour arriver à nos fins simplement, rien de tel qu’un papier quadrillé et un crayon. Il va nous servir à construire très simplement le repère et l’enchaînement des différents rectangles représentant les valeurs en pourcentage de l’espace occupé sur le disque.

Histoire de repères

Le système de coordonnées de SVG est assez classique en informatique. Le point (0,0) se trouve en haut à gauche. L’axe des abscisses croît de gauche à droite et l’axe des ordonnées croît de haut en bas. La figure 2 illustre ce système.

[fichier SVG]

Système de coordonnées SVG
Figure 2: Système
de coordonnées SVG

S’il est courant de rencontrer cette orientation, elle rentre cependant en opposition avec les habitudes prises sur les bancs de l’école. Ainsi, nos souvenirs de mathématiques nous conduisent à réfléchir avec un système un peu différent. Inventé au 17ème siècle par René Descartes, le système cartésien situe l’origine vers le bas de l’écran et si l’orientation de l’axe des abscisses ne changent pas, celui des ordonnées se retrouve inversé, comme l’illustre la figure 3.

[fichier SVG]

inversion
Figure 3: Système de
coordonnées cartésien

Alors comment passer de l’un à l’autre ? Comment réfléchir avec un repère cartésien et avoir un graphique correct ? Deux solutions sont envisageables. La première consiste à concevoir une fonction au niveau du langage de programmation qui fera la conversion des points exprimés dans le système cartésien en des points corrects dans le système de coordonnées de SVG. La seconde utilise les coordonnées cartésiennes directement dans le document SVG et définit les transformations à appliquer sur les éléments graphiques pour obtenir le résultat attendu. Nous allons opter pour la seconde solution. La première transformation fera donc une translation (fonction translate) de tous les points vers le bas. Nous utilisons une translation (30,130) qui correspond à une première translation de (30,30) pour éloigner le graphique du bord du document, à laquelle s’ajoute une translation de (0,100). 100 étant la valeur maximale des ordonnées.

Ensuite, il faut inverser l’axe des ordonnées en l’orientant de bas en haut. La fonction scale qui permet d’appliquer à chaque point un facteur multiplicatif. scale(1,-1) appliqué à chaque point permet de conserver l’abscisse et inverse l’ordonnée. Au niveau du document SVG, un simple bloc englobant permet d’appliquer les transformations à tous les composants graphiques situés à l’intérieur de ce bloc :

<g transform="translate(30,130) scale(1,-1)">
  <rect .../>
  <line .../>
</g>

 

Scalable Vector Graphics

Scalable Vector Graphics (SVG) est un langage XML, né en 1998 peu après la recommandation XML 1.0. Il est issu d’un groupe de travail du W3C. Il permet de …(lire la suite)

L’attribut transform définit la séquence des transformations. Elles sont définies les unes à la suite des autres en les séparant par un espace.

Simple, mais…

Cette transformation globale a l’avantage d’être simple, mais elle inverse tous les points, y compris ceux des caractères ! Toutes les lettres se retrouvent donc à l’envers, voir figure 4.

[fichier SVG]

Inversion des caractères
Figure 4: Inversion des
ordonnées et des caractères

Il faut donc soit les sortir du bloc de transformation et calculer leurs nouvelles coordonnées, soit leur appliquer de nouvelles transformations : scale(1,-1) permet de remettre à l’endroit les caractères. Mais elle fausse par la même occasion le calcul de coordonnées (x,y). Un caractère positionné sur le point P(20,60) se trouve après la première translation en un point P'(50,190) (grâce à la translation de (30,130)). Or il devrait se trouver à P''(50,70) soit 60 points plus haut que l’axe des abscisses (situé à y=130).

[fichier SVG]

Remise à l'endroit des caractères
Figure 5: Remise à l’endroit
des caractères

Le point se trouve donc deux fois trop bas, voir figure 5. Une translation de -60 permet de le remonter sur l’axe des abscisses (y=0 dans le repère cartésien). Une seconde translation de -60 lui permet de trouver sa place (y=60 dans le répertoire cartésien). Il faut donc effectuer un translation pour tous les textes positionnées à (Xorig,Yorig) de (0, -2*Yorig). Maintenant que ces quelques règles sont établies, toutes les données du graphique résultat pourront être exprimées en coordonnées cartésiennes.

Collecter les informations avec python

La collecte des informations du système peut être réalisée à l’aide d’une fonction récursive. Les paramètres à lui fournir sont le nom de répertoire de base et la profondeur de parcours dans l’arbre du système de fichiers. La fonction va alors calculer le poids d’un répertoire donné et stocker dans une liste un tuple d’informations si le niveau du répertoire est compatible avec ce que veut l’utilisateur. Par exemple, l’appel de la méthode gatherSize(r"d:\monrep", 2), recueillera toutes les informations relatives à l’occupation disque du répertoire d:\monrep. Le second paramètre permet de fixer une limite en profondeur afin de synthétiser l’information. Ainsi, les données d’un répertoire d:\monrep\niv2 seront disponibles alors que les données d’un répertoire d:\monrep\niv2\niv3 ne le seront pas. Pour les avoir, il suffit de fournir le nombre adéquat à la méthode, soit 3.

L’utilisation de Python, de son support des tuples, des listes et des fonctions associées comme la somme, le filtrage ou les fonctions lambda permet d’écrire une méthode relativement courte et simple. La somme des tailles de chaque fichier contenu dans un répertoire donné. Pour cela, la fonction listdir récupère la liste de tous les éléments, fichiers et répertoires. Un filtre permet de ne garder que les éléments de type fichier (à l’aide de la fonction de test os.path.isfile). La fonction sum est utilisée pour calculer le poids de cette liste nouvellement créée.

def gatherSize(self, directory, level, deep=1):
listdir = os.listdir(directory)
filelist = filter(lambda
   f: os.path.isfile(os.path.join(directory, f)), listdir)
weight = sum([os.path.getsize(
   os.path.join(directory, name))
   for name in listdir])

 

Python et les listes

S’il y a bien un concept sur lequel Python est éclatant, c’est bien le support natif des listes. Les listes, les fonctions lambda, …(lire la suite)

Puis la même fonction gatherSize est appelée de façon récursive sur chacun des sous-répertoires. De la même façon que pour la liste des fichiers, la liste de répertoires peut être obtenue très simplement à l’aide d’un filtre. Puis, il suffit de faire la somme de toutes les tailles ainsi calculées pour obtenir le poids de la sous-arborescence. Celui-ci ajouté au poids de l’ensemble des fichiers du répertoire constitue le poids total du répertoire passé en paramètre de gatherSize.

subdir = filter(lambda f: os.path.isdir(os.path.join(directory, f)), listdir)
subdir_w = sum([self.gatherSize(os.path.join(directory,f),
                      level,deep+1) for f in subdir])
total = weight+subdir_w

Enfin, il faut déterminer si la profondeur n’est pas trop importante par rapport au besoin de l’utilisateur, et si les informations sont stockées ou non. Une fois le parcours arborescent terminé, les informations sont disponibles sous la forme d’une liste de couples (nom,taille) qui représente la liste des répertoires trouvés.

Production du SVG

Produire du SVG n’est pas compliqué en soi, une fois le système de représentation des coordonnées choisi. Comme il s’agit d’un format XML, plusieurs approches permettent de générer un tel flux. La première consiste à écrire directement le XML sans utiliser de librairie. Rapide à mettre en oeuvre, du moins au départ, la gestion des guillemets rend vite le code illisible et il faut faire attention à produire du XML bien formé. Seconde solution, utiliser les librairies XML, telle que dom, minidom (fournies en standard) ou une libraire tierce (4Dom ou ElementTree). Cette approche permet de produire un XML bien formé en utilisant un code plus clair. Enfin, il existe des API SVG pour python. Elles permettent de s’abstraire facilement de l’implémentation XML sous-jacente et de se concentrer sur les aspects SVG. Celle choisie dans cet article est SVGDraw. Ce n’est pas la plus complète ni la plus complexe, mais elle a l’avantage d’être très simple et de ne constituer qu’un seul fichier à mettre dans le répertoire site-package. Qui plus est, la version windows s’auto-installe. La création du document SVG se fait en deux étapes. La première va créer l’enveloppe globale. Une section déclarative, définie par l’élément SVG defs, contient trois motifs (éléments SVG pattern). Ils serviront à représenter la graduation des axes et la grille de fond. La figure 6 illustre le motif représentant l’axe des abscisses.

 

[fichier SVG]

Motif pour la représentation des graduations de l'axe des abscisses
Figure 6: Motif pour la représentation des
graduations de l’axe des abscisses

Ce motif, d’une largeur de 20, définit deux traits verticaux. Le plus grand représentera toutes les graduations multiples de 20, le petit permettra d’identifier les graduations multiples de 10. Ces deux traits sont définis par des éléments SVG path. Outre les informations relatives au dimensionnement du motif et aux couleurs des traits, l’attribut le plus intéressant est la définition “d” du chemin. Elle consiste en une suite de commandes séparées par une virgule. Chaque commande est construite à l’aide d’une instruction désignée par une lettre (M,V,L,C,S,Q,T,...) et d’une ou plusieurs coordonnées (absolues ou relatives).

<pattern width="20"
   patternUnits="userSpaceOnUse"
   id="axeX" height="10">
   <path stroke="black"
         stroke-width="0.25"
         d="M 0 0, L 0 10" fill="none"/>
   <path stroke="lightgray"
         stroke-width="0.25"
         d="M 10 10, V 5" fill="none"/>
</pattern>

Le premier chemin est défini comme suit : Aller (M i.e Move) au point 0,0, faire une ligne (L i.e Lineto) jusqu’aux coordonnées absolues 0,10. La seconde ligne est définie par : Aller au point 10,10 et tracer une ligne verticale (V ou Vertical Lineto) jusqu’aux coordonnées absolues x,5 (la valeur de x ne change pas puisqu’il s’agit d’une ligne verticale). La spécification d’un attribut id=”axeX” permet de nommer ce motif et il pourra ensuite être utilisé de nouveau dans le graphique SVG, en faisant simplement référence à son nom. Le dessin de l’axe des x se fait donc ainsi :

<rect y="-10" width="240"
   fill="url(#axeX)" x="0" height="10"/>

Le rectangle est positionné plus bas que l’axe (avec des coordonnées cartésiennes), est large de 240 (le motif sera donc appliqué 12 fois) et est rempli avec le motif dont le nom correspond à “axeX”. Les motifs et les dessins effectifs de l’axe des Y et de la grille de fond sont construits sur le même principe.

Les lignes des axes sont dessinées à l’aide de l’élément SVG line et les deux flèches orientant les axes sont réalisées avec l’élément SVG polygon. Enfin, on affiche les informations générales sur le résultat de la recherche : le répertoire source et la taille globale utilisée. La seconde étape de la production du graphique va analyser chaque élément de la liste constituée dans la phase de collecte (voir ci-dessus) et ajouter un rectangle pour la représentation de la barre, et du texte pour afficher les informations relatives. On commence par trier la liste des couples (nom,taille). La taille occupée par le répertoire constitue le facteur de tri. Avec Python, le tri d’une liste constituée d’éléments complexes comme des tuples, des dictionnaires ou des objets se révèle très simple. Il suffit en effet de fournir une fonction de comparaison pour assurer l’opération :

def tupleCmp(self, a,b):
  (nameA, sizeA) = a
  (nameB, sizeB) = b
  return cmp(sizeB,sizeA)# tri des elements selon la taille occupee
self.infos.sort(self.tupleCmp)

Ici seules les tailles sont importantes, elles constituent donc le facteur de comparaison. Puisque c’est l’ordre décroissant qui nous intéresse, la fonction retournera donc 1 si sizeB>sizeA. Enfin, pour chaque élément de la liste, c’est à dire chaque répertoire, une barre est générée (via l’élément SVG rect) ainsi que deux zones de texte (éléments SVG text) : le pourcentage d’occupation et les informations sur le répertoire à proprement dit. Il suffit d’incrémenter à chaque fois l’axe des X, sans oublier d’ajouter les paramètres de transformation pour que les coordonnées cartésiennes exprimées dans la boucle soient cohérentes avec le système de coordonnées de SVG. Une fois, toutes les informations générées, le document SVG en mémoire est exporté vers un fichier XML temporaire. Le module webbrowser permet de lancer une instance du navigateur Internet avec l’URL du document ainsi construit. Le navigateur affiche alors le document SVG résultat. Il est donc nécessaire qu’il dispose du plugin approprié.

En une centaine de lignes de Python, nous avons pu analyser la structure d’un système de fichiers et produire un graphique vectorielle de haute qualité. Il serait simple de le rendre encore plus attractif en ajoutant d’autres éléments SVG. Par exemple, on pourrait obtenir un effet 3D des barres, ajouter une image dans la grille de fond, travailler les fontes avec des ombres, ou ajouter une dimension dynamique au graphique. Maintenant que les bases sont posées, libre à vous de laisser courir votre imagination…

Les sources de l’article

Liste et paramètre de fonction en python

Python et les paramètres de fonctions où comment faire la correspondance entre un type complexe et une liste de paramètres.

>>> def fct(p1,p2,p3,p4):
        print "p1=%s\tp2=%s\tp3=%s\tp4=%s"%(p1,p2,p3,p4)
>>> li=["pauillac", 1973, 1.0, 'a']
>>> fct(*li)
p1=pauillac     p2=1973 p3=1.0  p4=a
>>> apply(fct, li)
p1=pauillac     p2=1973 p3=1.0  p4=a
>>> di={”a”:1, “b”:2, “c”:3, “d”:4}
>>> fct(*di)
p1=a    p2=c    p3=b    p4=d
>>> apply(fct, di.keys())
p1=a    p2=c    p3=b    p4=d
>>> apply(fct, di.values())
p1=1    p2=3    p3=2    p4=4
>>> tup=(23,34,56,99)
>>> fct(*tup)
p1=23   p2=34   p3=56   p4=99

attention, il y a quand même un contrôle sur la taille entre ce qui est demandé et ce qui est fourni…

>>> fct(*li)

Traceback (most recent call last):
  File "<pyshell #43>", line 1, in -toplevel-
    fct(*li)
TypeError: fct() takes exactly 4 arguments (5 given)

Test graphique des fontes disponibles dans swing avec jython

L’utilisation de Jython pour tester Java est vraiment fort agréable. La syntaxe simplifiée de Jython permet de tester rapidement le jdk pour confirmer ou infirmer des suppositions quant à son fonctionnement.

Lister les fontes disponibles

Ici, j’avais besoin de connaître les fontes disponibles, et plutôt que de se lancer dans un programme Java me donnant l’information, les quelques lignes suivantes répondirent rapidement à mon besoin :

from java.awt import *

ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
for font in ge.getAllFonts():
    print font.getFontName()

Quatre lignes suffisent donc ! Le résultat est le suivant :

Arial
Arial Black
Arial Italique
Arial Gras
Arial Gras Italique
Comic Sans MS
Comic Sans MS Gras
Courier New
Courier New Italique
Courier New Gras
...

Test graphique des fontes

Avoir le nom de ces fontes en très peu de temps est déjà appréciable, mais compléter ce bout de programme pour avoir un rendu graphique, est encore mieux. Ainsi, il est beaucoup plus facile, de sélectionner la fonte désirée.

from java.lang import *
from java.awt import *
from javax.swing import *

testtext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890"
def windowClosingEvt(evt): System.exit(0)

# class enable to change the font of each label contained in the list
class FontRenderer(DefaultListCellRenderer):
    def getListCellRendererComponent(self, list_,value,index,isSelected,hasFocus):
        label = DefaultListCellRenderer.getListCellRendererComponent(self,list_,
            value,index,isSelected,hasFocus)
        label.setFont(Font(str(value), Font.PLAIN, 14))
        label.setText("%s : %s"%(str(value),testtext))
        return label

# create the main frame
fr = JFrame("Font tests", size=(400,400), windowClosing=windowClosingEvt)

# create the font list
ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
fontlist = map(lambda x:x.getFontName() , ge.getAllFonts())
j = JList(fontlist, cellRenderer = FontRenderer() )

#add the list to the frame and show it
fr.getContentPane().add(JScrollPane(j))
fr.visible=1

Le programme ci-dessus crée une liste (JList) et insére tous les noms des polices disponibles. Chaque ligne est bien sûr formattée à l’aide de sa propre fonte, ce qui permet d’avoir le rendu en même temps que le nom. Rien de spécial à ce code, mis à part un aspect intéressant de l’utilisation de la syntaxe Python : la manipulation des listes. Et plus particulièrement, celle de la fonction map. Ainsi

fontlist = map(lambda x:x.getFontName() , ge.getAllFonts())

permet d’appliquer une lambda expression (chargée de trouver le nom de la fonte) à chaque élément de la liste. Ce concept évite donc l’écriture d’une boucle sur chacun des éléments.

Le résultat de cette expression est une liste qui contient chaque élément transformé.

Test graphique des fontes Java

Le fichier de test fonts.py

Utiliser la transformation identité : Jython + JAXP

La transformation identité permet de générér un arbre xml résultat identique à celui fourni en entrée.

Ainsi, pour écrire un arbre DOM dans un flux, sans faire appel à des librairies externes, il est possible de n’utiliser que JAXP.

from org.w3c.dom import *
from javax.xml.parsers import *
from javax.xml.transform import *
from javax.xml.transform.dom import *
from javax.xml.transform.stream import *
from java.lang import System

# create the dom document
db = DocumentBuilderFactory.newInstance().newDocumentBuilder()
doc = db.getDOMImplementation().createDocument(None, "root", None)
doc.appendChild(doc.createComment("created by jaxp"));
doc.getDocumentElement().appendChild(doc.createElement("foo"))
doc.getDocumentElement().getFirstChild().appendChild(doc.createTextNode("bar"))
doc.getDocumentElement().appendChild(doc.createProcessingInstruction("myPI", "help :)"))

# generate output
tr = TransformerFactory.newInstance().newTransformer()
tr.transform(DOMSource(doc),StreamResult(System.out))

La première partie construit un arbre DOM simple. La seconde utilise la transformation identité pour diriger le resultat de la transformation sur la console. Ne passer aucune feuille de style au transformateur JAXP revient à lui indiquer d’utiliser la feuille de style identité.

Le résultat est le suivant :


<root><foo>bar</foo></root>