---
title: Faire sa première interface graphique
author: Steve Kossouho
---

# Faire sa première interface graphique avec Python

----

## Exemples d'outils pour les interfaces graphiques

Depuis la moitié des années 80, on a commencé à voir s'introduire dans le domaine public des 
ordinateurs livrés avec des interfaces graphiques, destinées à interagir simplement avec le système.
Avec le temps, les outils de programmation se sont peaufinés, au point qu'à ce jour, il existe de 
très nombreux outils avancés de développement pour créer des interfaces graphiques, même avec Python. 

Parmi eux, on rencontre fréquemment :

- [Qt](https://www.qt.io/) (PyQt ou PySide)
- [GTK](https://www.gtk.org/) (Python-GTK)
- [tkinter](https://docs.python.org/fr/3/library/tkinter.html) (bibliothèque standard)
- [wxWidgets](https://www.wxwidgets.org/) (wxPython)

----

### Choix de la communauté

Parmi les choix disponibles pour créer des interfaces, les développeurs utilisent très fréquemment
la bibliothèque Qt, de par la relative intuitivité de l'API que cette dernière propose. Également,
elle est fournie avec de nombreuses applications très utiles pour concevoir ses applications, qui
fonctionnent d'ailleurs assez bien sur de nombreux systèmes d'exploitation, comme Windows, 
Linux ou encore Mac OS.

----

### L'interface par défaut de Tkinter

![Tkinter sous Ubuntu](assets/images/gui-api-tkinter.png)

(_Il est possible d'appliquer des thèmes qui imitent un thème, comme Win XP, Win 10, etc._)

----

### Exemple simple d'application Linux avec PySide

![PySide sous Windows, Linux, Mac OS](assets/images/gui-api-pyside.png)

----

### Choix de bibliothèque pour la formation

Pour ce chapitre, nous allons utiliser la bibliothèque Qt. Avec Python, nous avons accès à plusieurs
bibliothèques qui calquent l'API proposée par la bibliothèque originale en C++, telles que `PyQt` ou
`PySide`.

Pour des raisons de prise en charge officielle (de contenu packagé, et probablement de licence),
le choix se porte depuis quelques années sur `PySide`.

- [Documentation de PySide](https://wiki.qt.io/PySideDocumentation)
- [Exemples PySide](https://wiki.qt.io/PySide_Example_Applications)

----

## PySide6 avec Python (sous Linux et Windows)

En principe, pour travailler avec PySide sous Windows et Linux, il vous suffira d'installer le
paquet avec l'outil `pip`, aucun autre téléchargement ne semble être requis.

(Une intégration du thème des applications GTK dans PySide nécessite souvent d'utiliser le paquet
`pyside` installable via le gestionnaire de paquets de votre distribution Linux, et d'installer
également une bibliothèque pour Qt destinée à imiter les thèmes GTK.)

----

## Installer PySide avec pip

```{.bash .numberLines}
pip install pyside6  # environ 200 Mo
```

La commande installe la bibliothèque précompilée pour votre système d'exploitation, avec des programmes de gestion 
inclus (**Qt Designer**, **Qt Resource Compiler**, etc.).

----

## Écrire une fenêtre simple

Pour afficher sa première application fenêtrée avec PySide, nous **devons** suivre quelques directives :

- Créer un objet `QApplication` (singleton). Il gère notre application Qt.
- Tous les widgets "vivront" automatiquement dans cet objet d'application.

```{.python .numberLines}
from PySide6.QtWidgets import QApplication, QMainWindow

if __name__ == "__main__":
    application = QApplication()
    window = QMainWindow()
    window.show()  # Affiche la fenêtre
    application.exec()  # essayez sans cette ligne...
```

----

## Concepts des interfaces graphiques Qt

Pour pouvoir parcourir plus en détail l'univers de **Qt**, il nous faut aborder certains concepts nécessaires pour un projet 
complet autour de la bibliothèque :

- `Signal` : représente un type d'événement disponible sur un widget, ex. _cliqué_, _texte modifié_, etc.
- `Slot` : fonction exécutable lorsqu'un `Signal` est émis.
- `Property` : attribut d'un widget défini sous forme de méthodes, ex. `QMainWindow.windowTitle`

----

## Concevoir graphiquement une fenêtre et l'utiliser avec Python

Il existe des outils graphiques pour concevoir vos fenêtres, et vous pouvez ensuite importer votre travail
dans vos propres applications Python, ce qui vous permet de ne vous occuper que du code métier (ou presque).

Lorsque vous installez `PySide` avec `pip`, le package vous propose en ligne de commande d'utiliser
l'application **Qt Designer**, un outil visuel qui vous aide à créer vos fenêtres et boîtes de dialogue.

```{.bash .numberLines}
pyside6-designer & # lancer l'outil depuis un terminal… retirez le "&" sous Windows
```

----

### Interface principale de Qt Designer

![Qt Designer en action](assets/images/gui-qtdesigner.png)

----

#### Zone principale

Lorsque vous utilisez Qt Designer, vous obtenez une interface **WYSIWYG** (What You See Is What You Get), 
vous permettant de configurer visuellement et précisément un widget (qu'il s'agisse d'une fenêtre ou d'autre chose).

Vous pouvez y sélectionner, redimensionner ou "reparenter" un widget. C'est beaucoup plus facile que 
de le faire à la main. Vous pouvez également y déposer de nouveaux widgets en effectuant un glisser-déposer depuis la **Boîte de widget**.

Note : Vous pouvez aussi générer du code Python via l'outil, mais il est assez inélégant.

----

#### Inspecteur d'objet

Le panneau d'inspecteur d'objet vous montre deux informations :

1. L'arborescence des widgets contenus dans votre fichier actuel;
2. Chaque élément est affiché sur deux colonnes, d'abord le nom du widget (ex. `pushButton`), puis son type (ex. `QPushButton`).

Il peut être utile pour sélectionner facilement un widget, par exemple lorsque ce dernier est difficile à sélectionner 
avec la souris dans la zone d'aperçu.

----

#### Éditeur de propriétés

L'éditeur de propriétés est un panneau traditionnel (comme dans Visual Basic 4) affichant la liste des propriétés disponibles sur un widget, par exemple ses _dimensions_, la _police de caractères à utiliser_, le _texte du bouton_ etc…

Les propriétés sont souvent disponibles à la fois en lecture et en écriture, mais certaines peuvent n'exister 
que pour consulter un état d'un widget (lecture seule).

----

### Générer un script Python depuis Qt Designer

L'outil Qt Designer permet de générer des fichiers Python qui construisent votre fenêtre. Vous pouvez par la suite utiliser
le code généré dans votre projet Python (même s'il y a mieux à faire).

TODO: Ajouter une capture d'écran


----

### Charger un widget enregistré au format .ui

Les fichiers `.ui` générés par Qt Designer sont en réalité des fichiers XML indiquant l'arborescence de l'interface et 
les propriétés à définir sur les widgets.

Également, ces fichiers contiennent des informations supplémentaires, comme les fichiers de ressources associés, mais
pour le cadre de la formation, nous ne sommes pas obligés de nous y intéresser. (ça peut attendre)

TODO: Promotion, Custom Signals et Slots, Ajout de propriétés etc.

```{.python .numberLines}
from PySide6.QtUiTools import QUiLoader

# N'oubliez pas l'application, naturellement
window = QUiLoader().load("fichier.ui")  # QMainWindow
```

----

## Interagir avec les contrôles

PySide propose un système simple de signaux (événements) et slots (fonctions cibles), qui permettent 
de réagir lorsque des événements se produisent sur des widgets. 

Pour associer au clic sur un bouton l'exécution d'une fonction, nous devons utiliser
une méthode `connect` sur une référence qui porte le nom d'un événement, par exemple `clicked` :

```{.python .numberLines}
from PySide6.QtWidgets import QPushButton

def some_function_reference():
    pass

button = QPushButton()
button.clicked.connect(some_function_reference)
```

Notez que PyCharm ne peut pas autocompléter `connect` à cause de la nature de la bibliothèque 
(écrite en C++, avec des _stubs_ incomplets pour Python). 
Ce problème semble ne pas pouvoir être résolu ni du côté de **JetBrains**, ni du côté de **Microsoft**. 
Il semble qu'il pourrait être résolu du côté de **Qt**, qui refuse de corriger ses stubs Python…

----

## Qt Designer : mise en page adaptée au redimensionnement de fenêtre

Sous Qt Designer (ou QtCreator), aucun moyen simple n'a l'air disponible pour associer un layout 
au widget central d'une fenêtre (c'est-à-dire, un widget de mise en page qui possède toujours les mêmes
dimensions que la fenêtre)…

Ça n'est pas intuitif, mais y parvenir est beaucoup plus simple qu'il n'y paraît.

----

![Fenêtre avec un layout non redimensionné](assets/images/gui-qtdesigner-layout-1.png)

----

- Vous pouvez cliquer droit dans un espace vide de la fenêtre, et ajouter un layout au **widget central** :

  - Allez sur le menu contextuel `Mettre en page` (ou `Lay out`);
  - Puis choisissez le layout utilisé pour le widget central (généralement `QGridLayout`);
  - Tous les éléments qui étaient présents dans le widget central vont se disposer dans le nouveau layout.

- Vous pouvez aussi sélectionner le widget central dans l'inspecteur puis cliquer sur un bouton de layout dans la barre d'outils de l'application.

Le layout étant associé au widget central de la fenêtre, il se redimensionnera avec la fenêtre.



----

![Tout mettre dans un layout associé au widget central](assets/images/gui-qtdesigner-layout-2.png)
