Henri H
Petit Biscuit
Avant de pouvoir utiliser PyQt il y'a un certain nombre d'installations à faire. La section suivante va vous guider à travers ce processus :
Sur Windows, PyQt s'installe comme n'importe quelle application ou librairie. Si vous avez installé python (et son gestionnaire de paquets pip) faîtes :
pip3 install pyqt5
Sur MacOS, il va vous falloir installer Homebrew. Homebrew est un gestionnaire de paquets qui va vous simplifier l'installation d'applications en ligne de commande. Ouvrez l'invite de commandes et tapez :
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Une fois que Homebrew est installé vous pouvez télécharger python3 et pyqt en exécutant les commandes:
brew install python3
brew install pyqt5 --with-python-3
Sur Debian ou Ubuntu Linux (et leurs dérivés), installez pyqt en utilisant le gestionnaire de paquets apt. PyQt est disponible pour la plupart des distributions. La commande suivante marche donc pour Ubuntu Linux mais, gestionnaire de paquets mis à part, elle devrait être similaire dans les autres distributions :
apt-get install python3-pyqt5
Une fois que vous avez tout installé, vous serez capables d'utiliser python3, python et d'importer pyqt en faisant import pyqt5.
Le coeur de votre application réside dans la classe QApplication. Toute application a besoin d'un objet de cette classe pour fonctionner. C'est dans cet objet que réside la boucle d'évènements (event loop) du programme. La boucle d'évènements gère toutes les interactions de votre interface.
Ouvrez un nouveau fichier, donnez lui le nom que vous voulez et tapez les instructions suivantes:
from PyQt5.QtWidgets import QApplication, QMainWindow
# On crée une instance d'application
app = QApplication([])
# On commence la boucle d'évènements,
# c-à-d on démarre l'application
app.exec()
# Que se passe t-il? Rien.
Votre application tourne bel et bien en arrière plan... Mais elle n'a aucune fenêtre ! La boucle d'évènements tourne donc à vide dans l'attente d'évènements qui n'auront jamais lieu.
Appuyez sur Ctrl-c pour couper l'application ou fermez votre console python (ou l'invite de commandes). Ouvrez-la de nouveau pour repartir d'un bon pied. Il est temps de modifier le code précédent...
Toute application a besoin d'au moins une fenêtre. En langage Qt on les appelle les QMainWindow. Une application peut même avoir plusieurs fenêtres. L'application s'arrête une fois que la dernière fenêtre est fermée.
from PyQt5.QtWidgets import QApplication, QMainWindow
# On passe une liste vide en argument
app = QApplication([])
# On crée notre fenêtre !
window = QMainWindow()
window.show() # IMPORTANT fenêtres invisibles par défaut
# On initialise l'application.
app.exec()
Menu Bar: La barre de menus.
Toolbars: La barre d'outils.
Dock Widgets: Conteneurs qui entourent l'élément principal.
Central Widget: La widget centrale.
Status Bar: La barre d'état.
Pour personnaliser la fenêtre la meilleure approche consiste à créer une classe fille de QMainWindow.
On importe ensuite les attributs et comportements de la classe mère grâce à __init__. Définissons notre propre classe basée sur QMainWindow. Appelons là Fenetre.
Le code suivant résume cette opération
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtCore import Qt
# La classe mère est QMainWindow. On la customize
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
# On définit un titre à la fenêtre
self.setWindowTitle("Première fenêtre")
self.label = QLabel("INCROYABLE!!!")
# Aligner le texte du label au centre
# setAlignment est une méthode de la classe
# widget. Pour en savoir plus: http://doc.qt.io/qt-5/qt.html
self.label.setAlignment(Qt.AlignCenter)
# Toute fenêtre vient avec son widget central.
# Ce code vise à mettre notre label au centre de "Fenetre"
self.setCentralWidget(self.label)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Toute interaction de l'utilisateur envers l'interface graphique provoque un évènement. Il y'a plusieurs sortes d'évènements, chacun représente une interaction (avec la souris, le clavier, etc...).
Une fois qu'un évènement a eu lieu, la boucle d'évènements l'attache à une fonction particulière qui va définir le comportement du programme. On appelle cette fonction particulière un slot.
Dans la page de documentation de la classe QWidget, un évènement concerne le changement de titre de la fenêtre il s'appelle: windowTitleChanged
Le code suivant attache l'évènement (windowTitleChanged: le titre de la fenêtre a changé) à une fonction qui affiche le nouveau titre dans la console; elle s'appelle: onWindowTitleChange.
Pour attacher un évènement a une fonction on utilise la fonction connect:
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtCore import Qt
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.windowTitleChanged.connect(self.onWindowTitleChange)
self.setWindowTitle("Mon incroyable App!")
self.label = QLabel("C'EST GENIAL!!!")
self.label.setAlignment(Qt.AlignCenter)
self.setCentralWidget(self.label)
def onWindowTitleChange(self, s):
print(s)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
self.windowTitleChanged.connect(self.onWindowTitleChange)
Cette ligne relie le signal windowTitleChanged au slot onWindowTitleChange. Le code du slot est lui aussi détaillé dans la classe Fenêtre mais il est à l'extérieur de la fonction __init__
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtCore import Qt
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.windowTitleChanged.connect(self.onWindowTitleChange)
self.setWindowTitle("Mon incroyable App!")
self.label = QLabel("C'EST GENIAL!!!")
self.label.setAlignment(Qt.AlignCenter)
self.setCentralWidget(self.label)
def onWindowTitleChange(self, s):
print("Le titre de la fenêtre a changé! ")
print(s)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Classe mère des composants graphiques de PyQt: champs de textes, barres de progressions, boutons, labels, fenêtres, etc...
setFixedWidth/setFixedHeight: Définir la largeur/la hauteur fixe
setMaximumSize/setMinimumSize: Définir la taille max/min
setLayout: Définir un layout
Plus d'infos sur: https://doc.qt.io/qt-5/qwidget.html
Les widgets sont les composants de PyQt avec lesquels un utilisateur peut interagir. Une interface utilisateur c'est plusieurs widgets assemblés à l'intérieur d'une fenêtre.
Qt vient avec beaucoup de widgets prédéfinis mais vous avez la possiblité de créer vos propres widgets.
Tous les éléments graphiques sont donc des widgets. En Qt ils sont organisés en différentes classes et sous-classes qui héritent les unes les autres.
Pour avoir une liste complète n'hésitez pas à consulter la documentation.
Le code de la page suivante affiche une liste de widgets...
Arborescence de classe des widgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QPushButton, QDial, QSlider, QSpinBox, QLineEdit
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Quelques widgets")
self.layout = QVBoxLayout() # Crée un quadrillage vertical pour placer nos widgets
# Liste de widgets prédéfinis
self.widgets = [QLabel, QPushButton, QDial, QSlider, QSpinBox, QLineEdit]
# On peut même boucler sur les widgets
for w in self.widgets:
self.layout.addWidget(w()) # Par contre ne pas oublier les parenthèses pour les instancier...
self.widget = QWidget() # Widget Neutre
self.widget.setLayout(self.layout) # On lui met un calque (layout)
self.setCentralWidget(self.widget) # On définit ce calque en calque central
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Gère les composants textes. Cette classe hérite de Qwidget.
font: Instancier une police de caractères.
setFont: Définir une police instanciée.
setText: Définir un texte.
setAlignment: Aligner le texte (en haut, en bas, à droite, au centre).
Plus d'infos sur: https://doc.qt.io/archives/qt-4.8/qlabel.html.
La widget QLabel représente un simple texte. On peut définir le texte lors de la création de la widget ou plus tard à l'aide de la méthode setText.
widget = QLabel("Hello")
widget = QLabel("1") # Le label est crée avec le texte 1
widget.setText("2") # Bon finalement ça sera le texte 2
On peut même définir une police et un alignement pour le texte:
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtCore import Qt
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Quelques widgets")
self.label = QLabel(" Hello World! ")
self.font = self.label.font() # instancier une police
self.font.setPointSize(30) # lui donner une taille et...
self.setFont(self.font) # ...l'attacher au label
self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.setCentralWidget(self.label)
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Pour aligner le texte (par exemple au centre) on utilise:
# On peut cumuler les paramètres non contradictoires
# Par exemple alignement du texte tout en haut à gauche
widget.setAlignment(Qt.AlignLeft | Qt.AlignTop)
widget.setAlignment(Qt.AlignCenter)
On peut aussi afficher une image grâce à un QLabel. Pour ça, il faut faire appel à setPixmap. En paramètre on lui passe un objet QPixmap avec comme paramètre le nom de l'image à créer:
widget.setPixMap(QPixMap('no.jpg'))
Si vous voulez adapter l'image à la fenêtre utilisez la méthode setScaledContents avec le paramètre à True sur le QLabel:
widget.setScaledContents(True)
Le code suivant permet d'afficher l'image no.png
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtGui import QPixmap
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Paint")
self.label = QLabel('Hello')
self.label.setPixmap(QPixmap('no.png'))
self.label.setScaledContents(True)
self.setCentralWidget(self.label)
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Widget qui désigne les cases à cocher
setCheckState: Coche la case ou là décoche.
Plus d'infos sur: https://doc.qt.io/archives/qt-4.8/qcheckbox.html
stateChanged: Signal envoyé quand la case change de valeur.
Le widget QcheckBox traite des cases à cocher par l'utilisateur. Pour savoir si une case est cochée ou non on utilise un signal stateChanged et un slot show_state :
from PyQt5.QtWidgets import QApplication, QMainWindow, QCheckBox
from PyQt5.QtCore import Qt
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Les cases à cocher")
self.checkbox = QCheckBox()
# On coche la case
self.checkbox.setCheckState(Qt.Checked)
self.checkbox.stateChanged.connect(self.show_state)
self.setCentralWidget(self.checkbox)
def show_state(self, s):
print(s == Qt.Checked) # True si c'est coché
print(s) # 0 Unchecked, 1 Partially checked, 2 Checked
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Quand on utilise setCheckState, il existe d'autres valeurs pour modifier l'état de la checkbox :
widget.setCheckState(Qt.Unchecked) # La case sera décochée
widget.setCheckState(Qt.Checked) # La case est cochée
Dès que l'utilisateur coche la case, la fonction show_state est appelée. Nous l'avons codée pour qu'elle affiche l'état de la checkbox dans la console.
Widget qui désigne les listes déroulantes d'options
addItems: Ajoute des nouvelles options.
Plus d'infos sur: https://doc.qt.io/qt-5/qcombobox.html
currentIndexChanged: Signal envoyé quand l'utilisateur change l'option.
La QComboBox est une liste déroulante. Elle correspond à la simulation de formulaires multi-options.
On ajoute des éléments grâce addItems.
Cette widget est liée au signal currentIndexChanged (l'utilisateur passe d'une option à l'autre). Dans la page suivante on associe ce signal à deux slots: index_changed et text_changed.
widget = QComboBox()
widget.addItems(["option 1", "option 2", "option 3"])
from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Les Combo Box")
self.combobox = QComboBox()
self.combobox.addItems(["Option 1", "Option 2", "Option 3"])
# Ce signal transmet l'index de l'option (pas son texte)
self.combobox.currentIndexChanged.connect( self.index_changed )
# Le même signal (un peu modifié) pour faire passer le texte
self.combobox.currentIndexChanged[str].connect( self.text_changed )
self.setCentralWidget(self.combobox)
def index_changed(self, i): # i est un int
print(i) # Affiche l'index de l'élément selectionné
def text_changed(self, s): # s est une string
print(s) # Affiche le texte de l'élément selectionné
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Autre exemple
# coding: utf-8
from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox
class FenetrePrincipale(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Un exemple de liste déroulante')
self.show()
# Une ComboBox permet de réaliser une liste déroulante
self.listeDeroulante = QComboBox(self)
self.listeDeroulante.addItem('Item 1')
self.listeDeroulante.addItem('Item 2')
self.listeDeroulante.addItem('Item 3')
self.listeDeroulante.currentIndexChanged.connect(self.methodePourGererLaListe)
self.setCentralWidget(self.listeDeroulante)
def methodePourGererLaListe(self, entierRecuParLeSignal):
print(self.sender().currentText())
print('L\'item d\'indice {0} a été sélectionné.'.format(entierRecuParLeSignal))
app = QApplication([])
maFenetrePrincipale = FenetrePrincipale()
app.exec()
Widget qui désigne les champs de texte
setMaxLength: Définit la longueur maximale.
setPlaceholderText: Définit un texte par défaut.
Plus d'infos sur: https://doc.qt.io/qt-5/qlineedit.html
textChanged: Signal envoyé quand l'utilisateur change le texte
Cette widget représente un simple champ de texte dans lequel l'utilisateur pourra entrer une valeur. Voici ses options :
widget = QLineEdit() # Création du Widget
widget.setMaxLength(10) # Longueur maximale
widget.setPlaceholderText("Enter your text") # Texte par défaut
# Décommentez cette ligne pour que le texte soit en lecture seule
#widget.setReadOnly(True)
Dans la page suivante on connecte ce widget à un slot à travers le signal textChanged.
from PyQt5.QtWidgets import QApplication, QMainWindow, QLineEdit
from PyQt5.QtCore import Qt
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Champ de texte")
self.lineEdit = QLineEdit()
self.lineEdit.setMaxLength(10)
self.lineEdit.setPlaceholderText("Entrez votre texte")
self.lineEdit.textChanged.connect(self.text_changed)
self.setCentralWidget(self.lineEdit)
def text_changed(self, s):
print("Le texte a changé !")
print(s)
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Widget qui désigne un champ numérique cliquable.
setMinimum: Définit la valeur minimale.
setMaximum: Définit la valeur max.
Plus d'infos sur: https://doc.qt.io/qt-5/qspinbox.html
valueChanged: Signal envoyé quand la valeur change.
Cette widget représente un simple champ numérique dont la valeur peut être augmentée ou baissée par le biais d'un clic sur des flèches.
widget = QSpinBox()
widget.setMinimum(-10) # Valeur minimale
widget.setMaximum(3) # Valeur maximale
Dans la page suivante on connecte ce widget à un slot à travers le signal valueChanged.
from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Champ de texte")
self.spinbox = QSpinBox() # QDoubleSpinBox pour les réels...
self.spinbox.setMinimum(-10)
self.spinbox.setMaximum(3)
self.spinbox.setSingleStep(3) # Le pas pour augmenter ou baisser
self.spinbox.valueChanged.connect(self.value_changed)
self.setCentralWidget(self.spinbox)
def value_changed(self, i):
print(i)
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Widget qui représente un curseur.
setMinimum: Définit la valeur minimale.
setMaximum: Définit la valeur max.
Plus d'infos sur: https://doc.qt.io/qt-5/qspinbox.html
valueChanged: Signal envoyé quand la valeur change.
Ce widget représente un curseur. Plus on avance vers le haut (ou la droite) et plus la valeur est élevée :
widget = QSlider()
widget.setMinimum(-20) # Valeur minimale
widget.setMaximum(20) # Valeur maximale
widget.setSingleStep(2) # Le pas pour augmenter ou baisser
Dans la page suivante on connecte ce widget à deux slots à travers les signaux sliderMoved (l'utilisateur a bougé le curseur) et sliderPressed (l'utilisateur a pressé le curseur).
from PyQt5.QtWidgets import QApplication, QMainWindow, QSlider
from PyQt5.QtCore import Qt
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Slider")
self.slider = QSlider()
# self.slider = QSlider(Qt.Horizontal)
# Décommentez ligne du haut pour slider horizontal
self.slider.setMinimum(-20)
self.slider.setMaximum(20)
# Le pas pour le changement de valeur, ici de 2 en 2
self.slider.setSingleStep(2)
self.slider.sliderMoved.connect(self.slider_position)
self.setCentralWidget(self.slider)
def slider_position(self, p):
print("position: ", p)
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Autre exemple
from PyQt5.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QSlider, QWidget
class FenetrePrincipale(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Un exemple de curseurs')
self.show()
self.widgetPourContenirLesCurseurs = QWidget(self)
self.disposition = QHBoxLayout()
self.widgetPourContenirLesCurseurs.setLayout(self.disposition)
self.curseur1 = QSlider()
self.curseur2 = QSlider()
self.curseur1.valueChanged.connect(self.methodePourGererUnCurseur)
self.curseur2.valueChanged.connect(self.methodePourGererUnCurseur)
self.disposition.addWidget(self.curseur1)
self.disposition.addWidget(self.curseur2)
self.setCentralWidget(self.widgetPourContenirLesCurseurs)
def methodePourGererUnCurseur(self, entierRecuParLeSignal):
print(self.sender().__dir__())
print('Le curseur a envoyé {0}.'.format(entierRecuParLeSignal))
app = QApplication([])
maFenetrePrincipale = FenetrePrincipale()
app.exec()
Widget qui désigne un bouton.
Plus d'infos sur: https://doc.qt.io/qt-5/qpushbutton.html
Clicked: Signal envoyé quand l'utilisateur clique sur le bouton
Cette widget représente un bouton. C'est typiquement le genre d'éléments dont on attend une interaction...
# Création d'un bouton
widget = QPushButton('Clic !')
Du coup la connexion à un slot vient évidemment à l'esprit. Le signal choisi sera tout simplement clicked comme l'illustre le code de la page suivante.
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Boutons")
self.button = QPushButton('Clic !')
self.button.clicked.connect(self.on_click)
self.setCentralWidget(self.button)
def on_click(self):
print("clic!")
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Plutôt que d'afficher les clics dans la console on veut voir s'afficher une boîte de dialogue. Pour cela on fait appel au widget QMessageBox:
# 1er paramètre: Type, 2ème: Titre, 3ème: Message
self.popup = QMessageBox(QMessageBox.Information,'Message','Bouton cliqué')
# Montrer la boîte de dialogue
self.popup.show()
D'autres types de boîtes de dialogues sont possibles: QMessageBox.Question, QMessageBox.Warning, QMessageBox.Critical.
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QMessageBox
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Bouton et boîte de dialogue")
self.button = QPushButton('Clic !')
self.button.clicked.connect(self.on_click)
self.setCentralWidget(self.button)
def on_click(self):
self.popup = QMessageBox(QMessageBox.Information,'Message','Bouton cliqué')
self.popup.show()
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Ajouter des couleurs à une fenêtre ou à un texte
setColor: Définit une couleur pour la palette.
Plus d'infos sur: https://doc.qt.io/qt-5/qpalette.html
On peut colorer deux types d'éléments. Les labels et les fenêtres. On utiliser deux méthodes similaires pour parvenir à ce résultat, grâce à la classe QColor:
# On définit une couleur en paramètres ("blue", "red", "green", etc...)
color = "red"
Pour colorer une fenêtre on utilise la constante QPalette.Window:
palette.setColor(QPalette.Window, QColor(color))
Pour colorer un texte on utilise la constante QPalette.WindowText:
palette.setColor(QPalette.WindowText, QColor(color))
Illustrons la définition d'une couleur dans la classe:
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Couleurs")
self.color = "green"
# On crée un nouvelle palette de couleurs
self.myPalette = self.palette()
# La palette devient la couleur passée en paramètres
self.myPalette.setColor(QPalette.Window, QColor(self.color))
# On associe notre palette au widget
self.setPalette(self.myPalette)
La commande optionnelle suivante vous assure de remplir la fenêtre:
self.setAutoFillBackground(True) # Pour remplir la fenêtre
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
from PyQt5.QtGui import QColor, QPalette
from PyQt5.QtCore import Qt
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Couleurs")
self.color = "red"
# On crée un nouvelle palette de couleurs
self.myPalette = self.palette()
# La palette devient la couleur passée en paramètres
self.myPalette.setColor(QPalette.Window, QColor(self.color))
# On associe notre palette au widget
self.setPalette(self.myPalette)
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
On peut colorer le texte en utilisant la même technique. On peut même colorer le texte en même temps que l'on colore le fond d'écran de la fenêtre.
Dans le code suivant on vous présente une technique qui utilise la méthode setStylesheet qui sert à personnaliser le style d'une widget
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel
from PyQt5.QtGui import QColor, QPalette
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Couleur Label")
self.label = QLabel(self)
self.label.setText('Bonjour')
self.color = "blue"
self.palette = self.label.palette() # Palette de Label !
self.palette.setColor(QPalette.WindowText, QColor(self.color))
self.label.setPalette(self.palette)
# Modification de la couleur de fond à l'aide d'une feuille de style
self.setStyleSheet('background-color: yellow')
self.setCentralWidget(self.label)
self.show()
app = QApplication([])
window = Fenetre()
window.show() # IMPORTANT!
app.exec()
Nous avons appris à nous servir de beaucoup de widgets, maintenant on aimerait être capables de les placer les uns par rapport aux autres dans la fenêtre.
Pour faire ça on utilise les layouts.
Nous allons nous servir de trois types de layout :
QHBoxLayout: Pour placer les éléments horizontalement.
QVBoxLayout: Pour placer les éléments verticalement.
QGridLayout: Pour placer les éléments avec une grille et des coordonnées.
QStackedLayout: Pour placer les éléments les uns devant les autres.
La QVBoxLayout permet d'aligner les éléments de manière verticale. A chaque ajout de widget, celui-ci se place tout en bas de la file:
L'ajout se passe de la manière suivante:
Pour illustrer l'utilisation des layouts, on réunit toutes les fonctionnalités dont nous avons parlé pour gérer les couleurs, dans une classe Color. Cette classe sert à définir des "rectangles de couleur" grâce à une QPalette.
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True) # Couleur d'arrière-plan
# On crée un nouvelle palette de couleurs
palette = self.palette()
# On ajoute la couleur passée en paramètres
palette.setColor(QPalette.Window, QColor(color))
# On associe notre palette au widget
self.setPalette(palette)
Pour créer un widget de couleur on a plus qu'à appeler notre classe avec comme paramètre la couleur que l'on souhaite:
# Rectangle rouge
Color('red')
# Rectangle bleu
Color('blue')
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
from PyQt5.QtGui import QColor, QPalette
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True) # Couleur d'arrière-plan
# On crée un nouvelle palette de couleurs
self.myPalette = self.palette()
# On ajoute la couleur passée en paramètres
self.myPalette.setColor(QPalette.Window, QColor(color))
# On associe notre palette au widget
self.setPalette(self.myPalette)
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Les Vertical Box Layout")
self.layout = QVBoxLayout()
self.layout.addWidget(Color('red'))
self.layout.addWidget(Color('blue'))
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
On peut paramétrer la manière dont on espace les éléments grâce aux méthodes setSpacing et setContentMargins associées au layout.
# Marge en haut, à droite, en bas, à gauche
layout1.setContentsMargins(0,0,0,0)
# Marge entre les éléments
layout1.setSpacing(20)
Les QHBoxLayout servent à organiser les widgets de manière horizontale:
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout
from PyQt5.QtGui import QColor, QPalette
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True) # Couleur d'arrière-plan
# On crée un nouvelle palette de couleurs
self.myPalette = self.palette()
# On ajoute la couleur passée en paramètres
self.myPalette.setColor(QPalette.Window, QColor(color))
# On associe notre palette au widget
self.setPalette(self.myPalette)
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Les Horizontal Box Layout")
self.layout = QHBoxLayout()
self.layout.addWidget(Color('red'))
self.layout.addWidget(Color('green'))
self.layout.addWidget(Color('blue'))
self.layout.setSpacing(0)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Le grid layout arrange vos éléments dans une grille. Chaque widget a ses coordonnées exprimées en ligne, colonne.
Vous n'êtes pas obligé de remplir toutes les cases de la grille...
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout
from PyQt5.QtGui import QColor, QPalette
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True) # Couleur d'arrière-plan
# On crée un nouvelle palette de couleurs
self.myPalette = self.palette()
# On ajoute la couleur passée en paramètres
self.myPalette.setColor(QPalette.Window, QColor(color))
# On associe notre palette au widget
self.setPalette(self.myPalette)
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Les Grid Layout")
self.layout = QGridLayout()
self.layout.addWidget(Color('red'),0,0)
self.layout.addWidget(Color('green'),1,0)
self.layout.addWidget(Color('blue'),1,1)
self.layout.addWidget(Color('purple'),2,1)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Insérez trois boutons en diagonale. Un clic sur un bouton provoque l'affichage d'une boîte de dialogue avec le numéro du bouton cliqué.
from PyQt5.QtWidgets import QApplication, QMainWindow, QGridLayout, QWidget, QPushButton, QMessageBox
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Boutons réactifs')
self.layout = QGridLayout()
for i in range(1,4):
self.bouton = QPushButton('B' + str(i), self)
self.bouton.clicked.connect(self.afficherNumeroBouton)
self.layout.addWidget(self.bouton, i, i)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
def afficherNumeroBouton(self):
texte = self.sender().text()
self.popup = QMessageBox(QMessageBox.Information, 'Message', 'Bouton ' + texte + ' cliqué.')
self.popup.show()
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Le QStackedLayout vous autorise à placer les éléments les uns devant les autres. Vous pouvez ensuite sélectionner le widget que vous voulez afficher. Vous pouvez vous servir de ce genre de fonctionnalités pour gérer une application graphique ou pour avoir un fonctionnement de type onglets.
Les index correspondent à l'ordre dans lequel les widgets ont été ajoutés dans la pile des widgets. Plus on en met, plus les derniers seront au premier plan.
On commence à compter à partir de zéro.
self.layout = QStackedLayout()
self.layout.addWidget(Color('red')) # 0
self.layout.addWidget(Color('green')) # 1
self.layout.addWidget(Color('blue')) # 2
self.layout.addWidget(Color('yellow')) # 3
On contrôle quelle widget on veut afficher grâce à setCurrentIndex.
self.layout.addWidget(Color('blue')) # Correspond à l'indice 0
self.layout.addWidget(Color('yellow')) # Correspond à l'indice 1
self.layout.setCurrentIndex(1) # Celui qui est devant sera 1 c-a-d yellow
On accède à l'index de celui qui est affiché grâce à la fonction currentIndex.
monIndex = self.stackedLayout.currentIndex()
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QStackedLayout
from PyQt5.QtGui import QColor, QPalette
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True) # Couleur d'arrière-plan
# On crée un nouvelle palette de couleurs
self.myPalette = self.palette()
# On ajoute la couleur passée en paramètres
self.myPalette.setColor(QPalette.Window, QColor(color))
# On associe notre palette au widget
self.setPalette(self.myPalette)
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Les QStackedLayout")
self.layout = QStackedLayout()
self.layout.addWidget(Color('red'))
self.layout.addWidget(Color('green'))
self.layout.addWidget(Color('blue'))
self.layout.addWidget(Color('yellow'))
self.layout.setCurrentIndex(2)
self.widget = QWidget()
self.widget.setMaximumSize(150,150)
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Il n'est pas toujours possible de voir les widgets qui sont "derrière" dans une QStackedLayout. Le code de la page suivante utilise un QPushButton pour passer d'un widget à l'autre...
Dans ce code on a omis la classe Color mais elle est toutefois nécessaire si vous voulez voir un rectangle coloré (n'oubliez pas de l'inclure).
from PyQt5.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QVBoxLayout, QWidget, QStackedLayout, QPushButton
from PyQt5.QtGui import QColor, QPalette
class Color(QWidget):
... # Copiez-collez le contenu de la classe Color ici
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("QStackedLayout avec boutons")
self.pageLayout = QVBoxLayout()
self.buttonLayout = QHBoxLayout()
self.stackedLayout = QStackedLayout()
self.pageLayout.addLayout(self.buttonLayout) # Les boutons horizontaux
self.pageLayout.addLayout(self.stackedLayout) # Le stacked layout
self.colors = ['red','green','blue','yellow']
n = 0
for color in self.colors:
btn = QPushButton( str(color) )
btn.clicked.connect(self.changeColor)
self.buttonLayout.addWidget(btn) # On ajoute le bouton
self.stackedLayout.addWidget(Color(color)) # On ajoute son widget
n += 1
self.widget = QWidget()
self.widget.setLayout(self.pageLayout)
self.setCentralWidget(self.widget)
def changeColor(self):
color = self.sender().text()
index = self.colors.index(color)
self.stackedLayout.setCurrentIndex(index)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Il existe aussi un widget spécial pour afficher les applications en "mode onglets". Il s'agit du QTabWidget.
Dans le code suivant nous avons utilisé la constante QTabWidget.East . Elle nous permet d'orienter les onglets selon les points cardinaux (ici ils seront à droite).
Grâce au paramètre setMoveable on peut déterminer si l'utilisateur est apte à déplacer les onglets ou non.
Et enfin le mode document permet d'avoir des onglets un peu plus fins pour ceux qui utilisent des ordinateurs macs.
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QWidget
from PyQt5.QtGui import QColor, QPalette
class Color(QWidget):
def __init__(self, color):
... # Copiez-collez le contenu de la classe Color ici
class Fenetre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Les onglets en Qt")
self.tabs = QTabWidget()
self.tabs.setDocumentMode(True)
self.tabs.setTabPosition(QTabWidget.East)
self.tabs.setMovable(True)
self.colors = ['red','green','blue','yellow']
for color in self.colors:
# On ajoute le widget directement AVEC son élément de tableau
self.tabs.addTab( Color(color), color)
self.setCentralWidget(self.tabs)
app = QApplication([])
window = Fenetre()
window.show()
app.exec()
Question 1: A l'aide de trois fichiers .py distincts faîtes trois fenêtres qui affichent les interfaces suivantes:
Question 2: Grâce à une QStackedWidget ou à un QStackedLayout, réunissez les trois interfaces dans une seule. Les boutons "previous" et "next" vous permettant de passer de l'un à l'autre
Question 1: Créer un fichier python qui affiche l'interface suivante:
Question 2: Grâce à une QPixmap, créer une interface qui affiche une photo avec en bas un bouton 'previous' et un bouton 'next'. Les boutons sont inertes pour l'instant...
Question 3: Faîtes en sorte que
l'appui sur 'previous' et 'next' permette de passer d'une image à l'autre (Slideshow).
Bonus: Maintenir le ratio des images de différentes tailles...
By Henri H
Créer des interfaces graphiques en python à l'aide de la librairie Qt