Interfaces graphiques

Selon Wikipedia, une interface graphique (en anglais GUI pour graphical user interface) ou environnement graphique est un dispositif de dialogue homme-machine.

Le dialogue s’effectue grâce à :

  • un écran qui affiche ou recueille l’information dans des fenêtres à travers des composants programmables (widgets) ;

  • et des dispositifs d’entrée-sortie (clavier, souris, trackpad, joystick, etc…) qui permettent à l’humain de fournir cette information à la machine.

Le développement d’interfaces graphiques peut se décomposer en 2 étapes principales :

  • positionner les widgets nécessaires dans la fenêtre graphique ;

  • puis paramétrer ces widgets pour les associer à une action spécifique lorsqu’ils sont activés (clic sur un bouton par exemple).

Ces opérations sont conduites en utilisant une bibliothèque graphique qui fournit l’environnement nécessaire à la programmation.

Il existe de nombreuses bibliothèques graphiques. Pour ce cours, nous travaillerons avec la bibliothèque Tcl/Tk interfacée avec Python via le module tkinter.

L’interfaçage permet d’utiliser des objets Python qui se chargent de produire les commandes Tcl/Tk de plus bas niveau.

Installation

Pour Windows et MacOS, les installations officielles de Python incluent le module tkinter et aucune autre opération n’est nécessaire.

Pour Linux/Ubuntu, pour économiser de la mémoire, tkinter n’est pas installé par défaut. Un package additionnel doit être installé

$ sudo apt-get install python3-tk

Quel que soit le système d’exploitation, si l’installation est opérationnelle, l’import de tkinter doit se passer sans erreur:

$ python
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import tkinter
>>>

L’exécution de la commande suivante doit provoquer l’apparition d’une fenêtre minimale permettant également de valider l’installation:

$ python -m tkinter

Une première fenêtre

Une fois tkinter opérationnel, on crée une fenêtre graphique :

  1. en important le package ;

  2. en instanciant la classe tkinter.Tk.

Dans un interpréteur interactif :

>>> import tkinter as tk
>>> window = tk.Tk()

Une fenêtre vide apparait à l’écran.


../_images/13-first-window.png

Pour le moment, une telle fenêtre ne dispose que des interactions avec le système d’exploitation (minimiser, agrandir, fermer) et n’est donc pas très utile.

Dans ce qui suit, on va ajouter des widgets permettant de la doter de capacités supplémentaires.

Les widgets

L’élément principal de l’interface graphique (GUI) est la fenêtre (window) construite à l’étape précédente. Pour ajouter des fonctionnalités à cette fenêtre, Tkinter dispose de nombreux widgets permettant de mettre en oeuvre des interactions avec l’utilisateur. La fenêtre est donc un container hébergeant l’ensemble des éléments graphiques (widgets) qui composent la GUI. Les principaux widgets sont :

Les principaux widgets

Widget Class

Description

Label

Utilisé pour afficher du texte ou des images

Button

peut être associé à une action lorsqu’il est cliqué

Entry

permet à l’utilisateur d’entrer une ligne de texte

Text

permet à l’utilisateur d’entrer plusieurs lignes de texte

Frame

un container rectangulaire permettant de grouper des widgets

Note

Les widgets de Tkinter sont parfaitement fonctionnels mais ont un look un peu daté. Les widgets tkinter.ttk sont plus modernes mais pour des raisons de simplicité, ils ne seront pas utilisés ici. Cependant, à la fin du cours, vous disposerez des connaissances nécessaires pour les mettre en oeuvre sans difficulté.

Chaque widget dispose de nombreuses propriétés permettant de définir leur apparence graphique ou leur comportement. Nous illustrerons ça dans les paragraphes qui suivent.

Le widget Label

Un Label est utilisé pour afficher du texte et des images. L’ajout d’un Label se fait en deux temps :

  • on crée une instance de la classe ;

  • et on l’ajoute à la fenêtre graphique avec un gestionnaire graphique. Ici on utilise la méthode pack() (on verra d’autres gestionnaires graphiques ultérieurement).

Ceci se fait avec les deux instructions suivantes

>>> label = tk.Label(text="Label")
>>> label.pack()

Note

La construction d’un widget est une condition nécessaire mais pas suffisante pour le faire apparaître à l’écran. Il faut que celui ci soit positionné avec un gestionnaire graphique.

Un des problèmes importants de la création de GUI est le dimensionnement des window et des widget. Ici un dimensionnement par défaut a été utilisé, consistant à adapter la taille de la fenêtre au widget qu’elle contient.

../_images/13-label.png

Le paradigme événementiel

La construction de GUI utilise le paradigme événementiel qui scrute indéfiniment les évènements liés à la fenêtre (clic sur un bouton, appui sur une touche du clavier, fermeture de la fenêtre, etc.) pour déclencher l’action liée à cet évènement.

Important

Le paradigme événementiel est très différent du paradigme impératif ou objet utilisés jusque là.

La boucle de scrutation est interne à tkinter. Ceci dispense l’utilisateur d’écrire sa propre boucle while :

  • dans un interpréteur interactif, la scrutation est déclenchée automatiquement. C’est cette scrutation qui fait que l’on peut fermer la fenêtre avec l’icone en haut à droite (interaction avec le système d’exploitation).

  • si le code est stocké dans un fichier, on rentre dans la scrutation des évènements en appelant explicitement la méthode mainloop(). Si cette méthode n’est pas appelée, la fenêtre n’apparait pas à l’écran.

Note

Pour le moment, la seule façon de mettre fin à la boucle de scrutation est de fermer la fenêtre .

Le code complet est stocké dans le fichier ci dessous.

 1# first_window.py
 2
 3import tkinter as tk
 4
 5window = tk.Tk()
 6
 7label = tk.Label(text="Hello world !")
 8label.pack()
 9
10window.mainloop()

On dispose maintenant d’une structure minimale qui :

  • crée une fenêtre principale (ligne 5);

  • lui ajoute un widget (lignes 7 et 8);

  • et démarre la scrutation d’évènements (ligne 10).

Paramétrer un Label

Un Label <https://tcl.tk/man/tcl8.6/TkCmd/label.html> dispose de nombreuses propriétés.

Note

La documentation décrit l’ensemble des propriétés du widget mais les exemples utilisent le langage de script Tcl. La transposition en Python est immédiate.

  • Tcl : label .t -text "Hello World !"    -bg red ;

  • Python : Label(text="Hello world !", bg="red").

Certaines propriétés sont communes à plusieurs widgets :

  • anchor : position du texte [n/ne/e/se/s/sw/w/nw/center]

  • background or bg : couleur de fond

  • borderwidth or bd : largeur de la bordure

  • font : police de caractères

  • foreground or fg : couleur du texte

  • image : affichage d’une image

  • padx : espace additionnel en X

  • pady : espace additionnel en Y

  • text : texte à afficher

D’autres sont spécifiques au tk.Label :

  • height : hauteur

  • state : état (normal/active/disabled)

  • width : largeur

Note

Les couleurs <https://tcl.tk/man/tcl8.6/TkCmd/colors.html>`_ sont définies par leur nom ou par leur code hexadecimal RGB:

hello = tk.Label(text="Hello world !", bg="aquamarine4")
hello = tk.Label(text="Hello world !", bg="#458B74")

Pour le choix des couleurs, on peut travailler avec des palettes prédéterminées, ou utiliser une Color Wheel pour plus de flexibilité.

Astuce

Les propriétés width et height sont spécifiées en unités de texte. La largeur et la hauteur du caractère 0 sont utilisées comme références. Comme ce caractère est plus haut que large, définir une largeur et une hauteur identique ne conduit pas à un widget carré !

Le widget Button

Un Button est un widget cliquable, permettant une interaction avec l’utilisateur. Quelques propriétés (communes à d’autres widgets, dont le Label):

  • anchor : position du texte [n/ne/e/se/s/sw/w/nw/center]

  • background or bg : couleur de fond

  • borderwidth or bd : largeur de la bordure

  • font : police de caractères

  • foreground or fg : couleur du texte

  • image : affichage d’une image

  • padx : espace additionnel en X

  • pady : espace additionnel en Y

  • text : texte à afficher

Quelques propriétés spécifiques :

  • command : la fonction associée au bouton

  • default : normal / active / disabled

  • height : hauteur

  • state : état (normal/active/disabled)

  • width : largeur

Le widget Entry

Un Entry <https://tcl.tk/man/tcl8.6/TkCmd/entry.html> est un widget permettant à l’utilisateur d’entrer un texte court. Quelques propriétés (communes à d’autres widgets):

  • background or bg : couleur de fond ;

  • borderwidth or bd : largeur de la bordure ;

  • font : police de caractères ;

  • foreground or fg : couleur du texte.

Quelques propriétés spécifiques :

  • state : état (normal/active/disabled)

  • width : largeur

Le widget Entry possède également des méthodes permettant d’interagir avec le contenu :

  • la méthode get() permet de récupérer le texte entré par l’utilisateur ;

  • la méthode delete() permet de supprimer le texte ;

  • et la méthode insert() permet d’insérer du texte.

Le widget Frame

Une Frame <https://tcl.tk/man/tcl8.6/TkCmd/frame.html> est un container contenant d’autres widgets. Son rôle est de structurer (et de simplifier) l’affichage en manipulant un ensemble de widgets à travers la Frame dans laquelle ils sont placés. La fenêtre principale de l’application est également un container et se comporte donc comme une Frame.

Une Frame possède un gestionnaire graphique, dont le rôle est de positionner les widgets selon un algorithme qui dépend de la méthode employée. La méthode pack() utilisée précédemment est un type de gestionnaire graphique.

Quelques propriétés d’une Frame :

  • background or bg : couleur de fond ;

  • borderwidth or bd : largeur de la bordure ;

  • cursor : le pointeur de souris utilisé lorsque la souris est à l’intérieur de la Frame ;

  • height : la hauteur de la Frame ;

  • padx : ajoute de l’espace supplémentaire horizontalement ;

  • pady : ajoute de l’espace supplémentaire verticalement ;

  • width : la largeur de la Frame ;

Les gestionnaires graphiques

La disposition des widgets dans une Frame (ou dans la fenêtre principale s’il n’y a pas de Frame) peut se faire suivant 3 méthodes:

  • pack();

  • grid() ;

  • place();

La méthode pack()

La méthode pack() permet de positionner les widgets les uns par rapport aux autres et est très efficace lorsqu’on ne souhaite pas un contrôle strict sur le layout de l’application.

Considérons le code ci dessous qui construit une fenêtre avec deux labels.

 1# 13-pack.py
 2
 3import tkinter as tk
 4
 5window = tk.Tk()
 6window.title('La méthode pack()')
 7window.geometry("350x200")
 8
 9label1 = tk.Label(window, text="Label 1", bg="green", fg="white")
10label2 = tk.Label(window, text="Label 2", bg="red", fg="white")
11
12label1.pack()
13label2.pack()
14
15window.mainloop()

../_images/13-pack-1.png

Le gestionnaire pack() place les labels dans l’ordre où il les rencontre. Il suffit d’intervertir les lignes 12 et 13 pour s’en convaincre.


../_images/13-pack-2.png

On peut passer des arguments à la méthode pack(), par exemple pour insérer un espacement à l’intérieur ou à l’extérieur des widgets.

 1# 13-pack.py
 2
 3import tkinter as tk
 4
 5window = tk.Tk()
 6window.title('La méthode pack()')
 7window.geometry("350x200")
 8
 9label1 = tk.Label(window, text="Label 1", bg="green", fg="white")
10label2 = tk.Label(window, text="Label 2", bg="red", fg="white")
11
12label1.pack(ipadx=10, ipady=10)
13label2.pack(padx=10, pady=10)
14
15window.mainloop()

../_images/13-pack-3.png

Les principales propriétés de la méthode pack() :

  • padx : ajoute de l’espace supplémentaire horizontalement (à l’extérieur du widget) ;

  • pady : ajoute de l’espace supplémentaire verticalement (à l’extérieur du widget) ;

  • ipadx : ajoute de l’espace supplémentaire horizontalement (à l’intérieur du widget) ;

  • ipady : ajoute de l’espace supplémentaire verticalement (à l’intérieur du widget) ;

  • filloption de remplissage de l’espace alloué au widget. 3 valeurs possibles :
    • tkinter.X : remplissage selon l’axe des x ;

    • tkinter.Y : remplissage selon l’axe des y ;

    • tkinter.BOTH : remplissage selon les deux axes.

  • expand : alloue de l’espace supplémentaire au widget ;

  • anchorpositionne le widget dans l’espace alloué. Plusieurs valeurs possibles :
    • N : en haut ;

    • S : en bas ;

    • E : à droite ;

    • W : à gauche ;

    • NW : en haut à gauche ;

    • NE : en haut à droite ;

    • SE : en bas à droite ;

    • SW : en bas à gauche ;

    • CENTER : au centre.

  • sidepositionne l’espace alloué au widget dans la Frame. 4 valeurs possibles :
    • tkinter.TOP ;

    • tkinter.LEFT ;

    • tkinter.RIGHT ;

    • tkinter.BOTTOM.

Exercice

Combiner les propriétés de la méthode pack() pour obtenir les figures ci dessous :

../_images/13-pack-4.png ../_images/13-pack-exercice-2.png

Une première application

Application

Développer une application permettant d’afficher le nombre de clics effectué sur un bouton. Ajouter une fonctionnalité permettant de mesurer l’intervalle de temps entre deux clics.

Application

Développer une application calculette permettant d’afficher le résultat des 4 opérations élémentaires.