Ajouter une interface graphique

Pour augmenter l’interactivité du jeu, nous allons ajouter une interface graphique (GUI). Nous allons utiliser la bibliothèque tkinter qui est incluse dans la distribution standard de Python.

Parmi toutes les technologies disponibles pour construire des interfaces graphiques en Python, tkinter est une bibliothèque multiplateforme, relativement simple et rapide à prendre en main.

La programmation d’une interface graphique avec tkinter se fait en deux étapes bien distinctes :

  • la création des widgets (boutons, champs de saisie, etc.) et leur placement dans une fenêtre principale ;

  • la définition des actions à effectuer lorsque l’utilisateur interagit avec les widgets.

Il s’agit d’un paradigme de programmation appelé event-driven programming ou programmation évènementielle. Le programme est en attente d’événements (clic de souris, appui sur une touche, etc.) et exécute des actions en réponse à ces événements.

C’est une nouvelle façon de programmer qui peut être déroutante au début. Il faut bien comprendre que le programme ne s’exécute pas de manière linéaire, mais qu’il est en attente d’événements. Le programmeur doit donc définir des actions à effectuer en réponse à ces événements.

Il s’agit d’un paradigme de programmation très utilisé pour les interfaces graphiques, mais également pour les jeux vidéos. Il diffère de ceux vus jusqu’à présent (programmation impérative et programmation orientée objet).

Un exemple simple

Pour se familiariser avec le concept de widgets et d’actions associées, nous allons créer une calculatrice graphique simple.

Un exemple d’application de calculatrice est disponible sur le site Real Python. L’application est ici construite pas à pas, de façon classique. Cette façon de faire est très fonctionnelle et installe durablement une compréhension des mécanismes mis en jeu.

On peut également utiliser ChatGPT pour obtenir un début d’architecture. Les mots clés à utiliser : calculator, tkinter, inheritance.

Note

Le terme inheritance est utilisé pour désigner l’héritage en programmation orientée objet. L’héritage permet de définir une classe fille qui hérite des propriétés d’une classe parente. Dans le cas de tkinter, la classe parente est tkinter.Tk. La classe fille est la classe que nous allons définir pour notre calculatrice. Elle héritera des propriétés de la classe tkinter.Tk.

L’image ci dessous montre un exemple de ce que l’on peut obtenir.

_images/calculator.png

Une fois le code généré, lancer l’application et analyser son fonctionnement. Identifier avec précision, ce qui fonctionne et ce qui ne fonctionne pas.

Analyser attentivement le code. Identifier la classe créée. Quelle est la classe parente ?

Comprendre le fonctionnement du constructeur. Que signifie l’appel à super() ?

Quelles sont les méthodes qui ont été générées ? Identifier le rôle de chacune d’entre elles.

Identifier les lignes de code qui définissent le constructeur de la classe. Quel est le rôle de ce constructeur ? Quelles sont les lignes de code qui sont exécutées lors de la création de l’objet ? Quel est le rôle de ces lignes de code ?

A quelle ligne du programme la classe est elle instanciée ?

A quelle ligne la méthode mainloop est elle appelée sur l’instance ? Elle n’est pourtant pas définie dans la classe, d’où provient elle ? Quel est le rôle de cette méthode ?

Il y a plusieurs architectures possibles. En voici une :

_images/calculator-uml.png

Pour celle ci, les attributs utilisés sont :

  • self.clear : identifie les situations où l’affichage doit être effacé ;

  • self.error : identifie les situations où l’expression à calculer n’est pas valide ;

  • self.buffer : contient l’expression à calculer ;

  • Tk.entry : la zone de saisie/affichage.

Les méthodes utilisées sont :

  • __init__() : le constructeur ;

  • button_click() : ce qui est réalisé lors du click d’un bouton ;

  • clear_entry() : efface la zone de saisie/affichage ;

  • calculate() : calcule l’expression saisie ;

  • create_buttons() : crée les différents boutons et leur affecte un comportement ;

  • bind_keys() : associe des touches du clavier à des actions ;

  • Tk.Entry.grid() : place la zone de saisie/affichage dans la fenêtre ;

  • Tk.Entry.delete() : efface le contenu de la zone de saisie/affichage ;

  • Tk.Entry.insert() : insère du texte dans la zone de saisie/affichage ;

  • Tk.Button.grid() : place un bouton dans la fenêtre ;

  • Tk.Button.configure() : configure un bouton ;

  • Tk.bind() : associe une action à un événement ;

  • Tk.mainloop() : lance la boucle d’attente d’événements.