Accès rapide :
La vidéo
Topographie d'une fenêtre Qt
Définir la zone centrale
Ajouts de widgets dans la zone centrale
Réagir aux interactions de l'utilisateur sur les widgets
Cette fois-ci nous allons voir comment ajouter divers widgets à votre fenêtre graphique Qt.
Outre le bandeau de la fenêtre, qui est géré par le Window Manager de votre système d'exploitation, une fenêtre Qt est divisée en plusieurs parties distinctes.
Au plus haut, on retrouve normalement l'espace associé à la barre de menu. En dessous se trouve la barre d'outils (il peut y en avoir plusieurs).
Ensuite on trouve la zone centrale : c'est elle qui contiendra les principaux widgets de votre application. Enfin, vous trouverez en bas de la fenêtre une barre
de statuts. Ces quatre zones sont directement gérées par la classe
QMainWindow
.
Pour les trois barres (menu, toolbar et statuts), si vous ne les demandez pas à la fenêtre, elles n'existeront pas et n'occuperont donc pas de place. Dans un telle cas, la zone centrale occupera tout l'espace de la fenêtre.
Pour le moment, nous allons ignorer les différentes barres (nous y reviendrons dans de prochains chapitres) et nous allons chercher à placer quelques widgets
(quelques composants graphiques) dans la zone centrale et, pour ce faire, nous allons commencer par la définir.
Dans l'exemple de code suivant, un widget de type QWidget
est injecté dans la fenêtre en tant que widget central : c'est la méthode
setCentralWidget
qui réalise l'injection.
Une couleur d'arrière-plan est définie pour ce widget afin de valider qu'il occupe bien tout l'espace disponible.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import sys from PySide6.QtGui import QIcon from PySide6.QtWidgets import QApplication, QMainWindow, QWidget class MyWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Central area") self.setWindowIcon(QIcon("icons/file.png")) self.resize(600, 300) # Le type QWidget représente un conteneur de widgets (et il est lui-même un widget). # On crée une instance que l'on va mettre au centre de la fenêtre. centralArea = QWidget() # On lui met une couleur d'arrière-plan, histoire de bien le voir. centralArea.setStyleSheet("background: #419eee") # On injecte ce widget en tant que zone centrale. self.setCentralWidget(centralArea) if __name__ == "__main__": app = QApplication(sys.argv) myWindow = MyWindow() myWindow.show() sys.exit(app.exec()) |
Et voici le résultat produit par l'exemple ci-dessus.
La librairie Qt vous propose un large choix de widgets (de composants graphiques). Vouloir tous les énumérer pourrait s'avérer long et fastidieux tellement la librairie est riche. Pour autant, le tableau ci-dessous présente quelques classes de widgets : elles seront utilisées dans la démonstration qui va suivre.
Nom de la classe | Description |
---|---|
QCalendarWidget | Un widget permettant de sélectionner une date sur un calendrier. |
QCheckBox | Une case à cocher. |
QLabel | Un label permettant d'afficher un message |
QLCDNumber | Un label ayant un visuel d'un écran à cristaux liquides. |
QLineEdit | Un champ de saisie de texte sur une seule ligne. |
QProgressBar | Une barre de progression permettant d'informer de l'avancement d'une tâche. |
QPushButton | Un bouton poussoir classique. |
QSlider | Une réglette permettant de choisir une valeur dans un intervalle donné. |
QSpinBox | Un composant de saisie de valeurs entières. |
Pour qu'un widget apparaisse dans la zone centrale plusieurs techniques peuvent être utilisées. La plus judicieuse serait d'utiliser un ou plusieurs layouts (des stratégies de positionnement de widgets) : nous parlerons de ces possibilités ultérieurement. Pour l'heure, je vais utiliser la technique la plus basique. Elle consiste à positionner un widget au pixel prêt, dans le conteneur.
Vous pouvez associer un widget à un conteneur en utilisant le constructeur du widget.
En règle générale, le dernier paramètre d'un constructeur de widget correspond à son conteneur (on peut aussi dire son parent, mais sans connotation liée à
l'heritage). Une fois le lien établi, il est possible de positionner et de dimensionner le widget en utilisant la méthode
setGeometry
.
1 2 3 4 |
# On place un calendrier dans la zone centrale. calendar = QCalendarWidget(centralArea) # On place le widget en (10, 10) et on le dimensionne avec 300 pixels de large et 200 pixels de haut. calendar.setGeometry(10, 10, 300, 200) |
setGeometry
.
Je propose dans cet exemple d'invoquer la méthode avec quatre paramètres (la position en x, la position en y, la largeur et la hauteur du widget).
Voici un exemple de code injectant un widget de chacun des types proposés ci-dessus.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import sys from PySide6.QtGui import QIcon, Qt from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QCalendarWidget, QLabel, \ QPushButton, QCheckBox, QSpinBox, QLCDNumber, QLineEdit, \ QSlider, QProgressBar class MyWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Some widgets") self.setWindowIcon(QIcon("icons/file.png")) self.resize(600, 300) # Le type QWidget représente un conteneur de widgets (et il est lui-même un widget). # On crée une instance que l'on va mettre au centre de la fenêtre. centralArea = QWidget() # On injecte ce widget en tant que zone centrale. self.setCentralWidget(centralArea) # On place maintenant chacun des composants souhaités dans la zone centrale. calendar = QCalendarWidget(centralArea) calendar.setGeometry(10, 10, 300, 200) lcd = QLCDNumber(self) lcd.display(1234) lcd.setGeometry(10, 220, 300, 70) label = QLabel("This is a label", centralArea) label.setGeometry(320, 10, 270, 30) button = QPushButton("This is a button", centralArea) button.setGeometry(320, 50, 270, 30) textBox = QLineEdit("This is a text box", centralArea) textBox.setGeometry(320, 90, 270, 30) checkBox = QCheckBox("This is a check box", centralArea) checkBox.setGeometry(320, 130, 270, 30) spinBox = QSpinBox(centralArea) spinBox.setValue(50) spinBox.setGeometry(320, 170, 270, 30) slider = QSlider(Qt.Horizontal, centralArea) slider.setValue(50) slider.setGeometry(320, 220, 270, 30) progress = QProgressBar(centralArea) progress.setValue(50) progress.setGeometry(320, 260, 270, 30) if __name__ == "__main__": app = QApplication(sys.argv) myWindow = MyWindow() myWindow.show() sys.exit(app.exec()) |
Et voici le résultat produit par cet exemple.
Qt propose les mécanismes de slots et de signaux pour vous permettre de réagir aux interactions de l'utilisateur sur les différents widgets. Précisons quelques points de terminologie.
Un signal correspond à un événement que le widget peut déclencher.
Un slot correspond à un gestionnaire d'événement. Plus précisément, il s'agit d'une méthode : il est conseillé d'adjoindre à une telle méthode
le décorateur @Slot
.
Une connexion permet d'associer un signal à un slot, en sachant qu'un même slot peut être connecté à plusieurs signaux et que plusieurs slots peuvent être connectés à un même signal.
Par exemple, si l'on cherche à réagir au clic sur le bouton de notre interface graphique, il nous faut connaître le signal associé.
Il s'agit du signal clicked
. Une fois le signal identifié, vous pourrez utiliser sa méthode connect
pour réaliser
la connexion à votre slot. Voici comment réaliser la connexion.
1 |
button.clicked.connect(self.buttonClicked) |
Et le slot devra être définit ainsi :
1 2 3 4 |
@Slot() # Pensez à importer le décorateur : from PySide6.QtCore import Slot def buttonClicked(self): btn = self.sender() print(f"Button <{btn.text()}> clicked") |
sender
permet de retrouver le widget ayant déclenché le
signal.
A titre d'exemple, nous allons chercher à réagir à la sélection d'une date dans le calendrier, au clic sur le bouton et à un changement de valeur sur le slider. Voici une nouvelle version du programme ajoutant les slots et les connexions nécessaires.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
import sys from datetime import date from PySide6.QtCore import Slot from PySide6.QtGui import QIcon, Qt from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QCalendarWidget, QLabel, \ QPushButton, QCheckBox, QSpinBox, QLCDNumber, QLineEdit, \ QSlider, QProgressBar class MyWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("Some widgets") self.setWindowIcon(QIcon("icons/file.png")) self.resize(600, 300) # Le type QWidget représente un conteneur de widgets (et il est lui-même un widget). # On crée une instance que l'on va mettre au centre de la fenêtre. centralArea = QWidget() # On injecte ce widget en tant que zone centrale. self.setCentralWidget(centralArea) calendar = QCalendarWidget(centralArea) calendar.setGeometry(10, 10, 300, 200) # On connecte le signal selectionChanged exposé par le calendier au slot dateSelected. calendar.selectionChanged.connect(self.dateSelected) lcd = QLCDNumber(self) lcd.display(1234) lcd.setGeometry(10, 220, 300, 70) label = QLabel("This is a label", centralArea) label.setGeometry(320, 10, 270, 30) button = QPushButton("This is a button", centralArea) button.setGeometry(320, 50, 270, 30) # On connecte le signal clicked exposé par le bouton au slot dateSelected. button.clicked.connect(self.buttonClicked) textBox = QLineEdit("This is a text box", centralArea) textBox.setGeometry(320, 90, 270, 30) checkBox = QCheckBox("This is a check box", centralArea) checkBox.setGeometry(320, 130, 270, 30) spinBox = QSpinBox(centralArea) spinBox.setValue(50) spinBox.setGeometry(320, 170, 270, 30) slider = QSlider(Qt.Horizontal, centralArea) slider.setValue(50) slider.setGeometry(320, 220, 270, 30) # On connecte le signal valueChanged exposé par le slider au slot valueChanged. slider.valueChanged.connect(self.valueChanged) progress = QProgressBar(centralArea) progress.setValue(50) progress.setGeometry(320, 260, 270, 30) @Slot() def dateSelected(self): calendar: QCalendarWidget = self.sender() print(f"Selected date is {calendar.selectedDate()}") @Slot() def buttonClicked(self): btn = self.sender() print(f"Button <{btn.text()}> clicked") @Slot(int) def valueChanged(self, value: int): print(f"Slider selected value is {value}") if __name__ == "__main__": app = QApplication(sys.argv) myWindow = MyWindow() myWindow.show() sys.exit(app.exec()) |
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :