Cours de Python - PyQt 5

version 1.1.0, dernière mise à jour le 17 novembre 2022.

   

Table des matières (TdM)

  1. I. Introduction
  1. II. Pour débuter…
    1. Une fenêtre vide…
  1. III. Créer une application
    1. Le squelette
    2. Widgets
      1. Widgets de base
      2. Remarque : cases à cocher mutuellement exclusives avec QRadioButton
  1. IV. Style et mise en page
    1. Changer le style des widgets
    2. Box layout
    3. Grille
    4. Application d'une mise en page à une fenêtre d'application
  1. V. Interagir avec la fenêtre
    1. Introduction
    2. Gestion des événements
      1. Principe
      2. Mise en œuvre sans gestionnaire
      3. Mise en œuvre avec un gestionnaire
    3. Accès aux champs de l'interface
      1. Champs simples
      2. Boutons radio
    4. Exercice : Mini-calculatrice

Retour au menu

Contenu du cours

I. Introduction

Python est un langage qui peut être utilisé pour réaliser des programmes évolués. Pour cela, il faut concevoir des interfaces utilisateur. Il existe plusieurs bibliothèques pour ce faire ; certaines ont été portées sur Python.

Nous allons aborder dans ce cours PyQt, pour laquelle il est plus facile de trouver un mode d'installation simple sous Windows. À la date de révision de ce cours, la bibliothèque est en version 6.2.2. 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.

II. Pour débuter…

1. Une fenêtre vide…

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.setGeometry(200,300,400, 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 », que l'on place à 200 pixels du bord gauche de l'écran, à 300 pixels à partir du haut, avec une largeur de 400 pixels, et une hauteur de 500 pixels. 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 Fenetre(QWidget) :
  def __init__(self) :
    super().__init__()
    self.lanceUI()

  def lanceUI(self) :
    self.setGeometry(200,300,400, 500)
    self.setWindowTitle("Titre de fenêtre")
    self.show()

monApp=QApplication(sys.argv)
w=Fenetre()
sys.exit(monApp.exec_())

Widget de base
Code source

La méthode super() permet de remonter à la classe dont on hérite.

>Retour à la TdM

III. Créer une application

1. Le squelette

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) #On définit une action à exécuter. Le & signifie qu'à l'affichage le E sera souligné et définit le raccourci clavier Alt-e
    exitAction.setShortcut('Ctrl+Q') #Définition d'un raccourci clavier>
    exitAction.setStatusTip("Quitter l'application") #Ce qui est affiché dans la barre de statut au survol par la souris
    exitAction.triggered.connect(qApp.exit) #Quand l'action est activée (« triggered »), cela lance l'événement prédéfini qApp.exit qui quitte l'application

    menu=self.menuBar() #La méthode prédéfinie menuBar permet de créer une barre de menu en haut de la fenêtre
    fichierMenu=menu.addMenu("&Fichier") #Définition d'une entrée de menu, qui affiche le texte Fichier avec comme raccourci clavier At-F
    fichierMenu.addAction(exitAction) #Rattachement de l'action définie plus haut sous cette entrée de menu

    self.barreOutils=self.addToolBar('barre1') #Création d'une barre d'outils nommée barre1. Ce nom peut servir éventuellement ailleurs dans l'application pour désigner la barre
    self.barreOutils.addAction(exitAction) #Rattachement de l'action définie plus haut dans cette barre d'outils

    self.setGeometry(300,300,500,250)
    self.setWindowTitle('Fenêtre principale')
    self.statusBar().showMessage("Barre de statut") #Texte affiché dans la barre de statut au chargement de l'application
    self.show()

monApp=QApplication(sys.argv)
fenetre=Principale()
sys.exit(monApp.exec_())

Application de base Qt5
Code source

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).

>Retour à la TdM

2. Widgets

a. Widgets de base

Les widgets sont les composants de base de l'application. En voici quelques-uns…

import sys
from PyQt5.QtWidgets import *
	
class Principale(QMainWindow):
	def __init__(self):
	super().__init__()
	self.setUI()
	
	def setUI(self):
	zoneTexte=QTextEdit() #Zone de saisie de texte
	btnOK=QPushButton("OK", self) #Un bouton
	zoneLigne=QLineEdit() #Un champ de saisie de texte
	label=QLabel("Champ texte") #Une étiquette
	case=QCheckBox("Case", self) #Une case à cocher
	combo=QComboBox(self) #Un menu de sélection
	
	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() #Mise en page
	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.setStyleSheet("background-color: gray")
	self.show()
	
monApp=QApplication(sys.argv)
fenetre=Principale()
sys.exit(monApp.exec_())
Quelques widgets
Code source

La plupart des noms sont explicites ; néanmoins voici quelques détails sur certaines méthodes :

Nous allons revenir sur la mise en page, limitée ici.

b. Remarque : cases à cocher mutuellement exclusives avec QRadioButton

QradioButton() permet de créer des cases à cocher, mais si l'on souhaite obtenir un comportement semblable aux « boutons radio » dans une interface Web, à savoir des cases mutuellement exclusives (quand on en coche une, les autres sont automatiquement décochées), il faut les regrouper avec QButtonGroup. Par exemple :

box = QHBoxLayout()

groupeCouleurs = QButtonGroup(self)

caseRouge = QRadioButton("Rouge")
caseVert = QRadioButton("Vert")
caseBleu = QRadioButton("Bleu")

groupeCouleurs.addButton(caseRouge)
groupeCouleurs.addButton(caseVert)
groupeCouleurs.addButton(caseBleu)

box.addWidget(caseRouge)
box.addWidget(caseVert)
box.addWidget(caseBleu)

>Retour à la TdM

IV. Style et mise en page

1. Changer le style des widgets

On peut changer l'apparence de n'importe quel widget, y compris la fenêtre principale, avec la méthode setStyleSheet, prenant en paramètre des propriétés CSS. Par exemple…

bouton=QPushButton("Un Bouton", self)
bouton.setStyleSheet("color: blue; background-color: gray; font-style: italic; font-variant: small-caps; font-family: serif")

… permet d'obtenir un bouton où le texte « Un Bouton » est écrit en bleu sur fond gris, en italique, petites majuscules et en police de caractères avec empattement (« serif »).

>Retour à la TdM

2. Box layout

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_())
Mise en page avec boîtes horizontales et verticales
Code source

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.

>Retour à la TdM

3. Grille

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_())
Mise en page avec grille
Code source

>Retour à la TdM

4. Application d'une mise en page à une fenêtre d'application

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)

>Retour à la TdM

V. Interagir avec la fenêtre

1. Introduction

Une personne utilisant le programme va pouvoir agir via l'interface de l'application. Cette dernière doit pouvoir permettre de capter et traiter les actions de l'utilisateur, par l'intermédiaire d'événements, mais aussi de modifier son état ou son affichage en réponse aux actions de l'utilisateur.

>Retour à la TdM

2. Gestion des événements

a. Principe

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 :

b. Mise en œuvre sans gestionnaire

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) #Qt.Horizontal indique que le slider doit être placé horizontalement
		
		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_())
Un slider et un affichage 7 segments
Code source

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.

c. Mise en œuvre avec un gestionnaire

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):
		print("Message envoyé")
		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_())
Un bouton
Code source
Une boîte d'information, avec pour titre Message d'alerte, un texte Bouton cliqué et un sous-titre 'avec deux boutons' et deux boutons OK et Cancel

On associe cette fois-ci au clic sur le bouton le gestionnaire afficheMessage, qui affiche tout d'abord un message dans la console, puis des informations diverses dans une boîte…

>Retour à la TdM

3. Accès aux champs de l'interface

a. Champs simples

Afficher en permanence les mêmes données ou les mêmes calculs ne serait pas très utile ; un minimum d'interactivité est requis pour créer une application.

Si l'on souhaite accéder à un widget depuis une fonction (par exemple un gestionnaire d'événements), il faut expliciter de quel objet il relève en le préfixant dans sa déclaration par self. Par exemple…

import sys
from PyQt5.QtWidgets import *
						
class Principale (QMainWindow) :
	def __init__(self) :
		super().__init__()
		self.setUI()
						
	def afficheMessage(self, event):
		print(self.texte1.text())
						
	def setUI(self):
						
		self.texte1=QLineEdit()
		bouton=QPushButton("OK", self)
		bouton.clicked.connect(self.afficheMessage)
		
		hbox=QHBoxLayout()
		hbox.addStretch()
		hbox.addWidget(self.texte1)
		hbox.addStretch()
		hbox.addWidget(bouton)
		hbox.addStretch()
		
		w=QWidget()
		w.setLayout(hbox)
		
		self.setCentralWidget(w)
		
		self.setGeometry(300,300,400,200)
		self.setWindowTitle('Affichage simple')
		self.show()
						
monApp=QApplication(sys.argv)
fenetre=Principale()
sys.exit(monApp.exec_())

(voir le code source). La méthode text() permet de lire la valeur saisie dans le champ. De manière similaire :

b. Boutons radio

La méthode checkedButton() de la classe QGroupButton permet de retrouver le bouton radio cliqué. Pour reprendre un des exemples précécents, pour afficher dans la console la valeur du bouton radio sélectionné dans le groupe groupeCouleurson fera…

def afficheValeur() :
  print(self.groupeCouleurs.checkedButton().text())

>Retour à la TdM

Exercice 1. Mini-calculatrice

Énoncé
Correction (Fenêtre sans interaction)
Correction (Saisie de nombres et affichage de calcul)
Correction (Remplacement par un affichage LCD)
Correction (Choix d'opération)

Historique de ce document

Conditions d'utilisation et licence

Creative Commons License
Cette création est mise à disposition par Gilles Chagnon sous un contrat Creative Commons.

Retour au menu