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

![Promotion de widget](assets/images/gui-qtdesigner-widget-promotion.png)

----

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
