<?xml version="1.0" encoding="utf-8"?>
<document>
   <titre>Python et PyQt - développement rapide d’application graphiques</titre>

<contenu>
<script type="text/javascript" src="imgopen.js"></script>

</head>

<body>

   <h2>Python et PyQt</h2>
   <p class="abstract">comme outils de développement rapide d’application graphique</p>
   
   <p>Python est un langage interprété et orienté objet. Il bénéficie d’une gestion
   automatique de la mémoire via un ramasse-miettes (<i>garbage collector</i>) et
   les variables sont typées dynamiquement.</p>

   
   <p>Pour toutes ces raisons, un programme écrit en Python est souvent bien plus court
   en terme de lignes de code que son équivalent C++ ou C. L’absence de phase de
   compilation ajoute encore au confort d’utilisation.</p>
   
   <p>Python rend les programmes plus simples et plus faciles à écrire,
   ce qui en fait un bon langage pour le développement rapide d’application.</p>
   
   <p>
   PyQt permet de lier le langage Python avec la bibliotèque Qt. Qt est
   un cadriciel (<i>framework</i>) qui permet de créer des interfaces graphiques
   multiplateformes. Le framework Qt offre également beaucoup d’autres services
   mais ce document s’arrête sur la manière de dessiner des interfaces graphiques 
   avec les outils Qt.
   </p>
   
   <h2>Un carnet d’adresses</h2>
   
   <p>Afin d’illustrer l’utilisation de Python et de PyQt, voici une application
   de carnet d’adresses qui permet d’enregistrer des personnes et leurs coordonnées dans
   une liste.</p>

  <p>Elle est <strong>écrite en 150 lignes de codes</strong> et respecte <a href="http://fr.wikipedia.org/wiki/Modèle-Vue-Contrôleur">le motif de conception <abbr title="Model View Controller">MVC</abbr> (Modèle, Vue, Contrôleur)</a>.</p>
   
   <img src="screenshots/app.png" alt="Capture d'écran de l’application">
   
   <ul>
<li><a href="tuto.zip">Télécharger les sources de l’application</a> ;</li>
<li><a href="standalone.zip">Télécharger l’application autonome (<i>standalone</i>) pour Microsoft Windows ainsi que les sources</a>.</li>
</ul>
   
   <h3>Qt Designer</h3>
   
   <p>Un outil de dessin d’interface comme Qt Designer apporte beaucoup d’avantages : </p>
   
   <ul>
      <li>Le programmeur ne doit pas écrire le code de l’interface qui est souvent complexe ;</li>
      <li>Le découplage de l’interface utilisateur et de l’application améliore l’abstraction et donc les composants sont réutilisables ;</li>
      <li>La traduction de l’interface utilisateur est facilitée.</li>
   </ul>
   
   <h4>Créer la fenêtre principale de l’application</h4>
   
   <p><span><img class='imgClosed' src="screenshots/start.png" alt="Créer un nouveau fichier Qt Designer"></span>
   Pour créer l’application il faut une fenêtre principale, choisissez "Main Window".</p>
   
   <p><span><img class='imgClosed' src="screenshots/main.png" alt="Main, caption"></span>
   Choisissez les options pas défaut pour obtenir une fenêtre avec une barre 
   de menu standard. Modifiez ensuite les propriétés <code>name</code> en <code>Main</code> et <code>caption</code> avec "carnet d’adresses" pour donner un titre à votre fenêtre.</p>
   
   <h4>Créer les composants graphiques</h4>

   
   <p><span><img class='imgClosed' class="imgClosed" src="screenshots/widgets.png" alt="Créer les composants graphiques"></span>
   Il faut maintenant créer les composants graphiques nécéssaires
   pour l’application.</p>
   
   <p><span><img class='imgClosed' class="imgClosed" src="screenshots/rename.png" alt="Créer les composants graphiques"></span>
   Renommez ensuite ces éléments avec des noms appropriés. Il est nécessaire d’utiliser
   des noms logiques afin de pouvoir accéder aux composants.</p>
   
   <h4>Finitions</h4>
   
   <p><span><img class='imgClosed' src="screenshots/final1.png" alt="Ajuster les composants graphiques"></span>
   Il manque encore la touche finale qui va permettre
   aux composants de bien se comporter en cas de redimensionnement de la
   fenêtre. Selectionnez un label et une ligne d’entrée de texte et
   choisissez un "layout horizontal". Répétez l’opération pour chaque
   paire label/ligne d’entrée de texte.</p>
   
   <p><span><img class='imgClosed' class="imgClosed" src="screenshots/final2.png" alt="Ajuster les composants graphiques"></span>Sélectionnez maintenant les paires label/ligne d’entrée de texte et les boutons "ajouter",
   "supprimer" et choisissez un "layout vertical".</p>
   
   <p><span><img class='imgClosed' class="imgClosed" src="screenshots/final3.png" alt="Ajuster les composants graphiques"></span>
   Faites un "horizontal layout" avec les deux autres boutons, puis un "vertical
   layout" avec le composant résultant et la liste. Vous pouvez ensuite
   faire un "horizontal layout (with splitter)" avec les deux composants
   restant ou simplement un "horizontal layout". Pour que le composant
   final prenne tout l’espace disponible il faut faire un "grid layout"
   sur la fenêtre principale. Pour finir, désactivez le bouton "modifier".</p>

   <p>Vous pouvez encore définir une taille minimale aux composants <code>label</code> afin d’avoir
   les lignes d’entrée de texte bien alignées.</p>

   <p>Il vous faut maintenant sauvegarder votre travail sous le nom <code>main.ui</code>. Les fichiers
   ".ui" sont des fichier XML de description d’interface.</p>
   
   <h3>Application en Python</h3>
   
   <h4>Générer le script pour l’interface (vue)</h4>
   
   <p>Afin de pouvoir utiliser les informations du fichier ".ui"
   de Qt Designer, il faut le traduire en Python. C’est le but
   du logiciel <code>pyuic</code>.</p>
   
   <p>Il faut donc faire en ligne de commande : <code><strong>pyuic main.ui > main.py</strong></code></p>
   
   
   <h4>La structure client (modèle)</h4>
   
   <p>L’objet client n’est qu’une structure de données qui permet de stocker les informations le concernant : </p>

   
<pre>class client:
    def __init__(self,nom,prenom,rue,npa,email):
        self.nom=nom
        self.prenom=prenom
        self.rue=rue
        self.npa=npa
        self.email=email
</pre>
   
   <h4>Le carnet d’adresses (modèle)</h4>
   
   <p>Le carnet d’adresses sert à lire le fichier de clients 
   et offre les méthodes nécessaires à sa modification, à sa 
   représentation et à sa sauvegarde : </p>
   
   
<pre>class carnet:
    def __init__(self,filename):
        self.clients = []
        <span># restauration du carnet de clients depuis le fichier "filename"</span>
        self.filename=filename
        if(os.path.exists(filename)):
            f = file(filename,'r')
            self.clients = pickle.load(f)
            f.close()
    
    <span># sauver la liste de clients dans le fichier</span>
    def sauver(self):
        f = file(self.filename,'w')
        pickle.dump(self.clients,f) 
        f.close()
        
    def ajouterClient(self, c):
        self.clients.append(c)
        
    def remplacerClient(self,index,c):
        self.clients[index]=c
        
    def client(self,indice):
        return self.clients[indice]
        
    def supprimer(self,indice):
        del self.clients[indice]
   
    <span># trie les clients selon la fonction de comparaison "sortFunc"</span>
    def trier(self):
        self.clients.sort(self.sortFunc)
    
    <span># fonction de tri sur les noms des clients</span>
    def sortFunc(self,x,y):
        return cmp(string.lower(x.nom),string.lower(y.nom))

    <span># représentation des clients sous forme de liste pour l’affichage</span>
    def listerClients(self):
        return [repr(client) for client in self.clients]    

    def __repr__(self):
        return "\n".join(self.listerClients())
</pre>

   <h4>L’application finale (contrôleur)</h4>

<pre>
#!/usr/bin/python
# -*- coding: utf8 -*-
import sys
import string
import pickle
import os
from qt import *
from main import *

class app:
    index=-1
    def __init__(self,args):
        <span># l’application Qt</span>
        self.qtapp=QApplication(args)
        <span># création du carnet</span>
        self.c = carnet('carnet.obj')
        <span># création de la fenêtre principale</span>
        self.win=Main()
        <span># affichage de la liste des clients dans la fenêtre</span>
        self.updateList()
        <span># affichage de la fenêtre</span>
        self.win.show()

        <span># connection SLOT/SIGNAL de Qt</span>
        self.qtapp.connect(self.win.fileSaveAction,SIGNAL("activated()"), self.c.sauver)
        self.qtapp.connect(self.win.fileExitAction,SIGNAL("activated()"),self.qtapp,SLOT("quit()"))
        self.qtapp.connect(self.win.ajouter,SIGNAL("clicked()"), self.ajouter)
        self.qtapp.connect(self.win.modifier,SIGNAL("clicked()"), self.modifier)
        self.qtapp.connect(self.win.supprimer,SIGNAL("clicked()"), self.supprimer)
        self.qtapp.connect(self.win.trier,SIGNAL("clicked()"), self.trier)
        self.qtapp.connect(self.win.liste,SIGNAL("highlighted(int)"), self.selectionListe)
        self.qtapp.connect(self.qtapp, SIGNAL("lastWindowClosed()"), self.qtapp, SLOT("quit()"))
        self.qtapp.exec_loop()

    <span># création d’un nouveau client en utilisant les champs courants de la fenêtre</span>
    def newClient(self):
        c = client(
            self.win.nom.text().ascii(),
            self.win.prenom.text().ascii(),
            self.win.rue.text().ascii(),
            self.win.npa.text().ascii(),
            self.win.email.text().ascii()
        )
        return c

    <span># bouton ajouter</span>
    def ajouter(self):
        self.c.ajouterClient(self.newClient())
        i=self.index
        self.updateList()
        self.win.liste.setSelected(i,True)
        
    <span># mettre la liste à jour</span>
    def updateList(self):
        self.win.liste.clear()
        self.win.liste.insertStrList(self.c.listerClients())

    <span># quand un client est selectionné dans la liste</span>
    def selectionListe(self,index):
        self.index = index
        self.win.nom.setText(self.c.client(index).nom)
        self.win.prenom.setText(self.c.client(index).prenom)
        self.win.rue.setText(self.c.client(index).rue)
        self.win.npa.setText(self.c.client(index).npa)
        self.win.email.setText(self.c.client(index).email)
        self.win.modifier.setEnabled(True)

    <span># bouton supprimer</span>
    def supprimer(self):
        if( self.index&gt;=0 ):
            i=self.index
            self.c.supprimer(self.index)
            self.updateList()
            self.win.liste.setSelected(i,True)

    <span># bouton modifier</span>
    def modifier(self):
        if( self.index&gt;=0 ):
            self.c.remplacerClient(self.index,self.newClient())
            i=self.index
            self.updateList()
            self.win.liste.setSelected(i,True)

    <span># bouton trier</span>
    def trier(self):
        self.c.trier()
        self.updateList()
        self.index=-1
        self.win.modifier.setEnabled(False)

def main(args):
    mapp = app(args)

if __name__=="__main__":
        main(sys.argv)

</pre>

<h2>Encore quelques mots</h2>

<p>Python et PyQt forment un excellent couple pour développer rapidement
des applications graphiques multiplateformes. L’intégration de Qt dans Python par PyQt est vraiment excellente.
On dirait presque qu’il s’agit du langage natif de Qt.</p>

<p>La licence d’utilisation de PyQt est liée à la version de Qt utilisée.
Avec la version 4 de Qt on devrait pouvoir réaliser des applications pour
Microsoft Windows sous licence GPL.
</p>

  </contenu>
<zone>

       <h3>Liens</h3>
       <ul>
       <li><a href="tuto.zip">Télécharger les sources</a> ;</li>
       <li><a href="standalone.zip">Télécharger l’application autonome (<i>standalone</i>) pour Microsoft Windows</a> ;</li>
       <li><a href="http://www.python.org/">Le langage Python</a> ;</li>
       <li><a href="http://www.riverbankcomputing.co.uk/pyqt/index.php">Les modules PyQt</a> ;</li>
       <li><a href="http://www.quadgames.com/download/pythonqt/">Télécharger PyQt avec Qt en GPL pour Microsoft Windows&trade;</a>.</li>
       </ul>


       <dl>
       <dt>Auteur</dt>
       <dd>Batiste Bieler</dd>
       <dt>Licence</dt>
      <dd>Les exemples de code sont sous <a href="http://fr.wikipedia.org/wiki/LGPL">LGPL</a></dd>
      <dd>Vous pouvez distribuer librement l’ensemble de ces documents en respectant le droit d’auteur.</dd>
       </dl>

   </zone>
</document>                                                                                                                                                                                                                                                                                                                       		