version 1.01, dernière mise à jour le 8 avril 2016.
Python est un langage qui peut être utilisé pour réaliser des programmes évolués. Pour cela, il faut pouvoir réaliser des interfaces utilisateur. Il existe plusieurs bibliothèques pour cela ; certaines ont été portées sur Python.
Tkinter est installé par défaut avec Python. C'est une bibliothèque déjà ancienne, provenant de Tk
PyGtK permet de produire des interfaces graphiques utilisant la bibliothèque GtK+ 2. Elle a été remplacée par PyGObject, qui utilise la bibliothèque GtK 3
PyQt et PySlide recourent elles à Qt, qui est une bibliothèque disponible sous deux licences différentes en fonction de leur utilisation pour la réalisation d'une application commerciale ou pas.
Nous allons aborder dans ce cours PyQt, pour laquelle il est plus facile de trouver un mode d'installation simple sous Windows. À la date d'écriture de ce cours, la bibliothèque est en version 5.5.1, compatible avec Python 3.4. Au-delà d'une bibliothèque pour la création d'une interface graphique, PyQt embarque aussi de nombreux composants, sous la forme de modules spécialisés. Nous n'utiliserons dans ce cours d'initiation que les modules QtCore et QtGui, parmi la vingtaine disponible.
PyQt5 permet de se simplifier la vie, et de coder simplement une interface utilisateur. Analysons l'exemple suivant...
import sys from PyQt5.QtWidgets import QApplication, QWidget monApp=QApplication(sys.argv) w=QWidget() w.resize(500,300) w.move(500, 500) w.setWindowTitle("Titre de fenêtre") w.show() sys.exit(monApp.exec_())
Dans cet exemple, on commence par importer les classes QApplication et QWidget du module PyQt5.QtWidgets. On définit ensuite une nouvelle application (monApp
), puis un « widget », auquel on donne une largeur de 500 pixels, une hauteur de 300 pixels, et que l'on place à 500 pixels du bord gauche de l'écran, et 500 pixels à partir du haut. On lui affecte ensuite un titre, puis on le montre. L'instruction sys.exit(monApp.exec_())
permet de quitter l'application en cliquant sur la croix telle qu'elle est définie par le système d'exploitation sur la fenêtre.
Si l'on veut concevoir de manière un peu plus orientée objet (ce qui permet de réutiliser un widget), on écrira plutôt...
import sys from PyQt5.QtWidgets import QApplication, QWidget class Fen(QWidget): def __init__(self): super().__init__() self.lanceUI() def lanceUI(self): self.resize(500,300) self.move(500, 500) self.setWindowTitle("Titre de fenêtre") self.show() monApp=QApplication(sys.argv) w=Fen() sys.exit(monApp.exec_())
La méthode super()
permet de remonter à la classe dont on hérite.
Créer un widget n'est pas créer une application, mais un composant d'une application. Les widgets sont positionnés à l'intérieur d'une fenêtre principale. En voici un exemple
import sys from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, qApp class Principale(QMainWindow): def __init__(self): super().__init__() self.setUI() def setUI(self): exitAction=QAction('&Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip("Quitter l'application") exitAction.triggered.connect(qApp.exit) menu=self.menuBar() fichierMenu=menu.addMenu("&Fichier") fichierMenu.addAction(exitAction) self.barreOutils=self.addToolBar('Quitter') self.barreOutils.addAction(exitAction) self.setGeometry(300,300,500,250) self.setWindowTitle('Fenêtre principale') self.statusBar().showMessage('Barre de statut') self.show() monApp=QApplication(sys.argv) fenetre=Principale() sys.exit(monApp.exec_())
La classe QAction
permet de définir des actions, que l'on peut attacher soit à une barre de menu (activée grâce à la méthode menuBar
), soit à une barre d'outils (méthode addToolBar
).
La défintion d'une action peut s'accompagner de son rattachement à un raccourci clavier, d'une information apparaissant dans la barre de statut (statusTip
), mais surtout à une action quand elle est activée (« triggered »). Ici, l'activation de l'action est rattachée à la méthode quit
de l'objet prédéfini qApp
, d'une manière similaire à l'attachement à un gestionnaire d'événements en JavaScript
.
On définit un menu par la méthode menuBar
. On ajoute à l'objet ainsi défini des éléments, auxquels on attache un texte (le caractère &
permet d'indiquer un raccourci clavier) ainsi qu'une action (ici celle définie précédemment)
La barre d'outils a un fonctionnement similaire.
Enfin, la méthode statusBar
permet d'accéder à la barre de statut de l'application
La méthode setGeometry
permet de spécifier la position et la taille de la fenêtre, en précisant dans cet ordre la position par rapport au coin supérieur gauche, largeur et hauteur.
Les widgets sont les composants de base de l'application. En voici quelques-uns...
import sys from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, qApp, QTextEdit, QPushButton, QHBoxLayout, QWidget, QVBoxLayout,QToolTip, QLineEdit, QLabel, QCheckBox, QComboBox class Principale(QMainWindow): def __init__(self): super().__init__() self.setUI() def setUI(self): zoneTexte=QTextEdit() btnOK=QPushButton("OK", self) zoneLigne=QLineEdit() label=QLabel("Champ texte") case=QCheckBox("Case", self) combo=QComboBox(self) btnOK.resize(btnOK.sizeHint()) btnOK.setToolTip("Ceci est un bouton <i>OK</i>") combo.addItem("Choix 1") combo.addItem("Choix 2") combo.addItem("Choix 3") combo.addItem("Choix 4") hbox=QHBoxLayout() hbox.addStretch(1) hbox.addWidget(label) hbox.addWidget(zoneLigne) hbox.addWidget(zoneTexte) hbox.addWidget(btnOK) hbox.addWidget(case) hbox.addWidget(combo) vbox=QVBoxLayout() w=QWidget() w.setLayout(hbox) self.setCentralWidget(w) #Définition des actions exitAction=QAction('&Exit', self) exitAction.setShortcut('Ctrl-Q') exitAction.setStatusTip("Quitter l'application") exitAction.triggered.connect(qApp.exit) menu=self.menuBar() fichierMenu=menu.addMenu("&Fichier") fichierMenu.addAction(exitAction) self.barreOutils=self.addToolBar('Quitter') self.barreOutils.addAction(exitAction) self.setGeometry(300,300,500,250) self.setWindowTitle('Fenêtre principale') self.statusBar().showMessage('Barre de statut') self.show() monApp=QApplication(sys.argv) fenetre=Principale() sys.exit(monApp.exec_())
La plupart des noms sont explicites ; néanmoins voici quelques détails sur certaines méthodes :
La méthode resize
appliquée à un objet de type QPushButton
permet de le redimensionner
La méthode sizeHint
laisse le soin à Qt de déterminer la taille optimale du bouton
Nous allons revenir sur la mise en page, limitée ici.
Les classes QHBoxLayout
et QVBoxLayout
permettent d'aligner des contenus horizontalement ou verticalement. Combinés, cela permet une grande souplesse. Supposons que l'on veuille par exemple ajouter trois boutons en bas de la fenêtre, répartis régulièrement :
import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QHBoxLayout, QVBoxLayout class Principale(QWidget): def __init__(self): super().__init__() self.setUI() def setUI(self): btn1=QPushButton("Bouton1") btn2=QPushButton("Bouton2") btn3=QPushButton("Bouton3") hbox=QHBoxLayout() hbox.addStretch(1) hbox.addWidget(btn1) hbox.addStretch(1) hbox.addWidget(btn2) hbox.addStretch(1) hbox.addWidget(btn3) hbox.addStretch(1) vbox=QVBoxLayout() vbox.addStretch(1) vbox.addLayout(hbox) self.setLayout(vbox) self.setGeometry(300,300,500,250) self.setWindowTitle('Fenêtre principale') self.show() monApp=QApplication(sys.argv) fenetre=Principale() sys.exit(monApp.exec_())
On commence par créer les trois boutons. On crée ensuite un layout horizontal avec QHBoxLayout
. La méthode addStretch
permet d'ajouter un espace de largeur variable, et qui s'ajuste en fonction de la largeur de la fenêtre. Ici, on insère donc un espace variable, un bouton, un espace variable, un deuxième bouton, un espace variable, le dernier bouton et un dernier espace variable.
On crée ensuite un layout vertical, auquel on ajoute un espace variable puis le layout horizontal que l'on vient de créer. Comme on n'ajoute rien en-dessous, le layout hbox
sera toujours calé sur le bas de la fenêtre.
Une telle mise en page permet de s'assurer que les boutons restent en permanence régulièrement répartis et calés en bas de la fenêtre.
Un autre système de mise en page commode est la grille, avec la classe QGridLayout
.
import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QGridLayout class Principale(QWidget): def __init__(self): super().__init__() self.setUI() def setUI(self): btn1=QPushButton("Bouton1") btn2=QPushButton("Bouton2") btn3=QPushButton("Bouton3") btn4=QPushButton("Bouton4") btn5=QPushButton("Bouton5") btn6=QPushButton("Bouton6") grille=QGridLayout() self.setLayout(grille) grille.addWidget(btn1, 1,1) grille.addWidget(btn2, 1,2) grille.addWidget(btn3, 1,3) grille.addWidget(btn4, 2,1) grille.addWidget(btn5, 2,2) grille.addWidget(btn6, 2,3) self.setGeometry(300,300,500,250) self.setWindowTitle('Fenêtre principale') self.show() monApp=QApplication(sys.argv) fenetre=Principale() sys.exit(monApp.exec_())
Les layouts ne peuvent être appliqués qu'à des widgets, pas à une classe héritée de QMainWindow
comme nous l'avons vu précédemment. Pour cela, il faut déclarer qu'un widget est le widget central de la fenêtre. Si on a défini un layout miseEnPage
(cela pourrait être par exemple l'objet grille
de l'exemple précédent), on écrira
centre=QWidget() centre.setLayout(miseEnPage) self.setCentralWidget(centre)
Il existe une multitude d'événements susceptibles de changer l'état d'une application ou des données qu'elle traite. Ces événements peuvent résulter d'une action de l'utilisateur (comme un clic), ou bien de l'activation d'une connexion Internet, du signal envoyé par une horloge, etc. Dans tous les cas, trois éléments sont concernés :
la source de l'événement, qui est l'objet changeant d'état (comme un bouton qui passe à l'état cliqué)
l'objet événement lui-même, qui porte des informations sur ce changement d'état
la cible de l'événement, c'est-à-dire l'objet qui va devoir réagir à la survenue de l'événement.
PyQt5 utilise les concepts de signal et de slot pour gérer les événements. Voici un exemple simple :
import sys from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLCDNumber, QSlider class Principale(QWidget): def __init__(self): super().__init__() self.setUI() def setUI(self): lcd=QLCDNumber(self) slider=QSlider(Qt.Horizontal, self) miseEnPage=QVBoxLayout() miseEnPage.addWidget(slider) miseEnPage.addWidget(lcd) self.setLayout(miseEnPage) slider.valueChanged.connect(lcd.display) self.setGeometry(300,300,200,100) self.setWindowTitle('Fenêtre principale') self.show() monApp=QApplication(sys.argv) fenetre=Principale() sys.exit(monApp.exec_())
QLCDNumber
et QSlider
sont des classes permettant d'afficher un nombre au format LCD et une barre de réglage. L'événement valueChanged
du slider est connecté à l'affichage du LCD.
On peut également, tout comme en JavaScript
, associer un gestionnaire d'événement :
import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QGridLayout, QMessageBox class Principale(QWidget): def __init__(self): super().__init__() self.setUI() def afficheMessage(self): message=QMessageBox() message.setText("<b>Bouton cliqué</b>") message.setInformativeText("avec deux boutons...") message.setWindowTitle("Message d'alerte") message.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) message.exec() def setUI(self): btn=QPushButton("Bouton", self) btn.clicked.connect(self.afficheMessage) self.setGeometry(300,300,100,30) self.setWindowTitle('Fenêtre principale') self.show() monApp=QApplication(sys.argv) fenetre=Principale() sys.exit(monApp.exec_())
On associe cette fois-ci au clic sur le bouton le gestionnaire afficheMessage
, qui affiche des informations diverses dans une boîte...
Cette création est mise à disposition par Gilles Chagnon sous un contrat Creative Commons.