147 lines
5.6 KiB
Markdown
147 lines
5.6 KiB
Markdown
---
|
||
title: Interfaces graphiques avec Pyside
|
||
author: 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).
|
||
|
||
```python
|
||
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 :
|
||
|
||
```python
|
||
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 :
|
||
|
||
```python
|
||
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.
|
||
|
||
----
|
||
|
||
## CSS dans les widgets
|
||
### Propriétés CSS
|
||
### Appliquer correctement son CSS
|
||
### Appliquer depuis un fichier
|
||
## Ressources
|
||
### Créer une ressource
|
||
### Compiler une ressource
|