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 DOM

Nous allons, dans ce tuto voir comment manipuler un fichier de données au format XML via l'API DOM (Document Object Model). Un DOM est une arborescence de noeuds en mémoire. Chaque noeud correspond à une partie du document et notamment à tag (mais pas que).

La librairie DOM pour Python est localisée dans le modulee xml.dom.minidom.

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

Les éléments d'un DOM

Il est important de comprendre qu'un DOM ne contient pas que des noeuds correspondant à des tags. Il y a aussi les noeuds de textes, les noeuds de commentaires, ... Selon la manière de programmer, il se peut que vous rencontriez quelques problèmes. L'exemple ci-dessous affiche tous les noeuds enfants (directes) du tag Shapes. Contrairement à ce que vous pourriez penser, il n'y a pas quatre noeuds enfants, mais 9. Effectivement, entre chaque noeud de tags se trouvent des noeuds de textes correspondants aux séquences de textes utilisées pour le formatage du document XML (retours à la ligne, tabulations, ...). Testez bien ce petit exemple.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
#!/usr/bin/python3

from xml.dom.minidom import parse, Node, Element


try:
    document = parse("Shapes.xml")
  
    shapesNode = document.documentElement
    nodes = shapesNode.childNodes
    print("Node count == " + str(len(nodes)))
    for node in nodes: 
        # Vous pouvez ignorer les noeuds de textes
        # en décommentant une des deux lignes suivantes.
        #if node.nodeType != Node.ELEMENT_NODE: continue
        #if not isinstance( node, Element ): continue
        
        print(node)
    
except:
    print("XML File is not well-formed")
Mise en évidence des noeuds de textes

Et voici le résultat produit par ce programme.

Node count == 9
<DOM Text node "'\n\t\n\t'">
<DOM Element: Circle at 0x7f9f1ad7d638>
<DOM Text node "'\n\n\t'">
<DOM Element: Square at 0x7f9f1ad7d768>
<DOM Text node "'\n\n\t'">
<DOM Element: Circle at 0x7f9f1ad7d898>
<DOM Text node "'\n\n\t'">
<DOM Element: Square at 0x7f9f1ad7d9c8>
<DOM Text node "'\n\t\n'">

Une autre solution équivalente consiste à utiliser shapesNode.getElementsByTagName("tagName") à la place de shapesNode.childNodes. Effectivement, getElementsByTagName ne renvoie que et uniquement que des Element (des tags), ce qui évite de filtrer les noeuds de textes.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
#!/usr/bin/python3

from xml.dom.minidom import parse, Node, Element


try:
    document = parse("Shapes.xml")
  
    shapesNode = document.documentElement
    circles = shapesNode.getElementsByTagName("Circle")
    print("Node count == " + str(len(circles)))
    for circle in circles: 
        print(circle)
    
except:
    print("XML File is not well-formed")
Utilisation de getElementsByTagName

Notre parseur XML

Maintenant que vous avez bien compris le problème de noeuds de textes, on peut afficher nos figures géométriques. Voici le code.

 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 
#!/usr/bin/python3

from tkinter import Canvas, Tk
from xml.dom.minidom import parse, Node, Element


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

        self.__canvas = Canvas(self, width=400, height=400, background="white")
        self.__canvas.pack()
        self.__parseData()


    def __parseData(self):
        try:
            document = parse("Shapes.xml")

            shapesNode = document.documentElement
            
            circles = shapesNode.getElementsByTagName("Circle")
            for circle in circles: 
                radius = int(circle.getAttribute("radius"))
                x1 = int(circle.getAttribute("x")) - radius
                y1 = int(circle.getAttribute("y")) - radius
                x2 = int(circle.getAttribute("x")) + radius
                y2 = int(circle.getAttribute("y")) + radius
                colorNode = circle.getElementsByTagName("Color")[0]
                color = "#%02x%02x%02x" % (int(colorNode.getAttribute("red")), 
                                           int(colorNode.getAttribute("green")), 
                                           int(colorNode.getAttribute("blue")))
                self.__canvas.create_oval(x1, y1, x2, y2, fill=color)
            
            squares = shapesNode.getElementsByTagName("Square")
            for square in squares: 
                length = int(square.getAttribute("length")) /2
                x1 = int(square.getAttribute("x")) - length
                y1 = int(square.getAttribute("y")) - length
                x2 = int(square.getAttribute("x")) + length
                y2 = int(square.getAttribute("y")) + length
                colorNode = square.getElementsByTagName("Color")[0]
                color = "#%02x%02x%02x" % (int(colorNode.getAttribute("red")), 
                                           int(colorNode.getAttribute("green")), 
                                           int(colorNode.getAttribute("blue")))
                self.__canvas.create_rectangle(x1, y1, x2, y2, fill=color)

        except:
            print("XML File is not well-formed")
        
    
window = MyWindow()
window.mainloop()
Notre parseur DOM

Voici le résultat produit par cette application.