Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Manipulation d'un fichier XML via l'API SAX

Nous allons, dans ce tuto voir comment manipuler un fichier de données au format XML via l'API SAX (Simple Api for Xml). Cette API est basée sur modèle événementiel : le parseur SAX va parcourir le document XML et pour chaque élément syntaxique qu'il va rencontrer, il va invoquer une « callback » (une méthode) correspondant au type de l'élément syntaxique.

Le fichier XML de données

Commencons par parler du fichier de données : il est au format XML et contient des définitions de figures géométriques. Le but du programme sera de lire les données de chaque figure puis de les tracer dans une interface graphique de type Tkinter. Voici ce fichier.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
<?xml version="1.0" ?>
<Shapes>
    
    <Circle x="100" y="100" radius="20">
        <Color red="255" green="0" blue="255" />
    </Circle>

    <Square x="300" y="100" length="50">
        <Color red="255" green="0" blue="0" />
    </Square>

    <Circle x="200" y="200" radius="30">
        <Color red="0" green="0" blue="255" />
    </Circle>

    <Square x="100" y="300" length="40">
        <Color red="0" green="255" blue="0" />
    </Square>
    
</Shapes>
Le fichier de données au format XML

Le type ContentHandler

La librairie SAX pour Python est localisée dans le paquetage xml.sax.

Le fonctionnement d'un parseur SAX repose notamment sur la classe ContentHandler. Celle-ci définie les différentes « callback », les différentes méthodes qui seront invoquées lors du traitment du fichier XML. Notez bien qu'à ce niveau, les méthodes ne font encore rien : il sera nécessaire de dériver cette classe pour dire quoi faire de concrêt. Voici le contenu de cette classe (je l'ai volontairement simplifié et j'ai aussi supprimer les commentaires.)

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
class ContentHandler:
    def startDocument(self):
        pass

    def startElement(self, name, attrs):
        pass

    def characters(self, content):
            pass

    def endElement(self, name):
        pass

    def endDocument(self):
        pass
        
    # . . .
Extrait de la classe xml.sax.ContentHandler

Notre parseur XML

Cette exemple de code est un peu subtile : il met en oeuvre de l'héritage multiple. Nous avons une classe, MyWindow, qui est à la fois une fenêtre (grâce à l'héritage de la classe Tk) et à la fois un handler SAX (grâce à l'héritage de ContentHandler). Cette fenêtre/handler contient un Canvas (une zone de tracè) dans lequel nous allons dessiner les figures géométriques.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
#!/usr/bin/python3

from tkinter import Canvas, Tk
from xml.sax import parse
from xml.sax.handler import ContentHandler


class MyWindow(Tk, ContentHandler):
    
    def __init__(self):
        Tk.__init__(self)
        self.title("Shaper V1.0")

        self.__canvas = Canvas(self, width=400, height=400, background="white")
        parse("Shapes.xml", self)
        self.__canvas.pack()
    
    
    def startDocument(self):
        print( "Start document parsing" )


    def startElement(self, name, attrs):
        if name=="Circle":
            self.__currentShape = dict(attrs)
        elif name=="Square":
            self.__currentShape = dict(attrs)
        elif name=="Color":
            self.__currentShape["color"] = "#%02x%02x%02x" % \
                (int(attrs["red"]), int(attrs["green"]), int(attrs["blue"]))

    
    def endElement(self, name):
        if name=="Circle":
            radius = int(self.__currentShape["radius"])
            x1 = int(self.__currentShape["x"]) - radius
            y1 = int(self.__currentShape["y"]) - radius
            x2 = int(self.__currentShape["x"]) + radius
            y2 = int(self.__currentShape["y"]) + radius
            self.__canvas.create_oval(x1, y1, x2, y2, fill=self.__currentShape["color"])
            print("Draw Circle")
        elif name=="Square":
            delta = int(self.__currentShape["length"]) / 2
            x1 = int(self.__currentShape["x"]) - delta
            y1 = int(self.__currentShape["y"]) - delta
            x2 = int(self.__currentShape["x"]) + delta
            y2 = int(self.__currentShape["y"]) + delta
            self.__canvas.create_rectangle(x1, y1, x2, y2, fill=self.__currentShape["color"])
            print("Draw Square")            

    
    def endDocument(self):
        print( "End document parsing" )
    

window = MyWindow()
window.mainloop()
Notre parseur SAX

Voici le résultat produit par cette application.