doSimple Python et PyQt - développement rapide d’application graphiques

Dernière modification le 11 February 2006

Python et PyQt

comme outils de développement rapide d’application graphique

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 (garbage collector) et les variables sont typées dynamiquement.

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.

Python rend les programmes plus simples et plus faciles à écrire, ce qui en fait un bon langage pour le développement rapide d’application.

PyQt permet de lier le langage Python avec la bibliotèque Qt. Qt permet de créer des interfaces graphiques multiplateformes. 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.

Un carnet d’adresses

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.

Elle est écrite en 150 lignes de codes et respecte le motif de conception MVC (Modèle, Vue, Contrôleur).

Capture d'écran de l’application

Qt Designer

Un outil de dessin d’interface comme Qt Designer apporte beaucoup d’avantages :

Créer la fenêtre principale de l’application

Créer un nouveau fichier Qt Designer Pour créer l’application il faut une fenêtre principale, choisissez "Main Window".

Main, caption Choisissez les options pas défaut pour obtenir une fenêtre avec une barre de menu standard. Modifiez ensuite les propriétés name en Main et caption avec "carnet d’adresses" pour donner un titre à votre fenêtre.

Créer les composants graphiques

Créer les composants graphiques Il faut maintenant créer les composants graphiques nécéssaires pour l’application.

Créer les composants graphiques 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.

Finitions

Ajuster les composants graphiques 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.

Ajuster les composants graphiquesSélectionnez maintenant les paires label/ligne d’entrée de texte et les boutons "ajouter", "supprimer" et choisissez un "layout vertical".

Ajuster les composants graphiques 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".

Vous pouvez encore définir une taille minimale aux composants label afin d’avoir les lignes d’entrée de texte bien alignées.

Il vous faut maintenant sauvegarder votre travail sous le nom main.ui. Les fichiers ".ui" sont des fichier XML de description d’interface.

Application en Python

Générer le script pour l’interface (vue)

Afin de pouvoir utiliser les informations du fichier ".ui" de Qt Designer, il faut le traduire en Python. C’est le but du logiciel pyuic.

Il faut donc faire en ligne de commande : pyuic main.ui > main.py

La structure client (modèle)

L’objet client n’est qu’une structure de données qui permet de stocker les informations le concernant :

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

Le carnet d’adresses (modèle)

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 :

class carnet:
    def __init__(self,filename):
        self.clients = []
        # restauration du carnet de clients depuis le fichier "filename"
        self.filename=filename
        if(os.path.exists(filename)):
            f = file(filename,'r')
            self.clients = pickle.load(f)
            f.close()
    
    # sauver la liste de clients dans le fichier
    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]
   
    # trie les clients selon la fonction de comparaison "sortFunc"
    def trier(self):
        self.clients.sort(self.sortFunc)
    
    # fonction de tri sur les noms des clients
    def sortFunc(self,x,y):
        return cmp(string.lower(x.nom),string.lower(y.nom))

    # représentation des clients sous forme de liste pour l’affichage
    def listerClients(self):
        return [repr(client) for client in self.clients]    

    def __repr__(self):
        return "\n".join(self.listerClients())

L’application finale (contrôleur)

#!/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):
        # l’application Qt
        self.qtapp=QApplication(args)
        # création du carnet
        self.c = carnet('carnet.obj')
        # création de la fenêtre principale
        self.win=Main()
        # affichage de la liste des clients dans la fenêtre
        self.updateList()
        # affichage de la fenêtre
        self.win.show()

        # connection SLOT/SIGNAL de Qt
        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()

    # création d’un nouveau client en utilisant les champs courants de la fenêtre
    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

    # bouton ajouter
    def ajouter(self):
        self.c.ajouterClient(self.newClient())
        i=self.index
        self.updateList()
        self.win.liste.setSelected(i,True)
        
    # mettre la liste à jour
    def updateList(self):
        self.win.liste.clear()
        self.win.liste.insertStrList(self.c.listerClients())

    # quand un client est selectionné dans la liste
    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)

    # bouton supprimer
    def supprimer(self):
        if( self.index>=0 ):
            i=self.index
            self.c.supprimer(self.index)
            self.updateList()
            self.win.liste.setSelected(i,True)

    # bouton modifier
    def modifier(self):
        if( self.index>=0 ):
            self.c.remplacerClient(self.index,self.newClient())
            i=self.index
            self.updateList()
            self.win.liste.setSelected(i,True)

    # bouton trier
    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)

Encore quelques mots

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 bonne et le système de "SIGNAL/SLOT" est pleinement fonctionnel.

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.

Liens

Auteur
Batiste Bieler
Licence
Les exemples de code sont sous LGPL
Vous pouvez distribuer librement l’ensemble de ces documents en respectant le droit d’auteur.