5.6 KiB
title, author
title | author |
---|---|
Interfaces graphiques avec Pyside | Steve Kossouho |
Développer avec Pyside et Qt Designer
Fichiers .ui et utilisation avec Python
Nous avons vu précedemment comment concevoir graphiquement des fenêtres dans l'outil Qt Designer,
puis comment les exploiter dans notre code Python. Nous avons d'abord utilisé la classe QUiLoader
et récupéré un objet de type QWidget
(généralement de type QMainWindow
précisément).
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication
def button_slot():
"""Fonction utilisée comme slot."""
pass
if __name__ == '__main__':
app = QApplication()
window = QUiLoader().load("file.ui")
button = window.button
button.clicked.connect(button_slot)
window.show()
...
app.exec()
Problème de l'approche "non objet"
Cette approche simple ne permet malheureusement pas de profiter de certaines fonctionnalités essentielles. Par exemple, il n'est pas possible de définir des slots via de simples fonctions dans lesquels il serait possible de retrouver un widget qui a généré un signal.
La raison principale à cette catégorie de problèmes provient du fait que l'application Qt est gérée via l'objet
QApplication
dans un thread distinct. Ce thread n'est pas en mesure de transmettre certaines références au processus
principal de votre programme, notamment les références d'objets générant un signal.
Le seul moyen correct de procéder (assez mal documenté) afin que votre code gérant vos widgets soit connecté au thread
de l'application Qt : Coder la gestion de votre fenêtre dans une classe héritant au moins d'une classe de la bibliothèque
Qt (ex. QObject
). Hériter d'une telle classe vous offre d'ailleurs une méthode essentielle pour retrouver une référence
d'objet ayant généré un signal : sender()
Voici un exemple de code correct important une conception graphique d'un fichier .ui :
from PySide6.QtCore import QObject, Slot
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget
class WindowManager(QObject):
"""Classe gérant l'interface graphique et contenant des slots."""
window: QMainWindow | QWidget = None
button: QPushButton = None
def __init__(self, *args, **kwargs):
# Ligne nécessaire pour exécuter l'initiation du QObject
super().__init__(*args, **kwargs)
self.window = QUiLoader().load("fichier.ui")
self.button = getattr(self.window, "button")
# Configurer le clic sur le bouton peut être configuré dans Designer
self.button.clicked.connect(self.button_clicked)
@Slot() # marquer correctement la méthode comme slot
def button_clicked(self):
"""Slot du clic sur le bouton."""
print(self.sender())
if __name__ == '__main__':
application = QApplication()
manager = WindowManager()
manager.window.show()
application.exec()
Dans l'exemple précédent, plusieurs éléments importants ont été rigoureusement respectés :
- La classe de gestion de l'interface et des slots hérite de
QObject
au moins; - les slots sont décorés par la classe
Slot
, qui possède une méthode__call__
renvoyant un décorateur (parenthèses nécessaires);
Utiliser des widgets personnalisés dans Designer
Supposons que vous avez rédigé du code pour créer votre propre widget (soit personnellement dessiné soit composé d'autres sous-widgets). Vous souhaitez utiliser Qt Designer pour concevoir votre interface, faisant usage dudit widget.
Malheureusement pour vous, seuls les widgets standard proposés par la bibliothèque Qt seront disponibles dans la boîte à outils
de Qt Designer. Heureusement, il est possible de personnaliser votre conception pour intégrer vos propres classes de widgets.
Pour cela vous devez insérer dans l'interface un widget duquel hérite votre classe personnalisée (ex. QFrame
) puis le "promouvoir".
Pour cela, sélectionnez votre widget dans l'interface d'aperçu ou dans l'explorateur d'objet avec un clic droit.
Cliquez ensuite sur l'option Promouvoir en…
. Une boîte de dialogue listant les widgets déjà promus s'affichera et vous proposera
de promouvoir le widget que vous venez de sélectionner.
Vous devez sélectionner la classe de widget de base (probablement celle déjà utilisée pour le widget à promouvoir),
puis indiquer le nom de la classe de widget à utiliser à l'exécution. Le nom du fichier d'en-tête n'est utilisé que pour
le langage C++, donc vous pouvez y saisir un point (.
).
Une fois le fichier .ui regénéré, il vous faudra configurer votre objet QUiLoader
pour y enregistrer les classes de
widgets personnalisées :
from PySide6.QtUiTools import QUiLoader
from mymodule import MyWidgetClass
loader = QUiLoader()
# Register custom widget class so that the promoted widget works.
loader.registerCustomWidget(MyWidgetClass)
widget = loader.load("fichier.ui")
Au chargement du fichier XML, les références du nom de classe généreront correctement les instances du type approprié. Notez que si votre classe de widget définit des propriétés supplémentaires par rapport à la classe dont elle hérite, vous pouvez les ajouter et les initialiser dans le panneau de l'éditeur de propriétés de Qt Designer.