Afin de pouvoir personnaliser votre classeur sans détruire le classeur sur lequel travaille votre voisin, vous allez tout d'abord aller dans le menu File puis Make a copy.... Renommez le classeur en ajoutant votre nom à la fin du nom de fichier par exemple.

TKinter 1 - Hello world

Concept d’interface graphique

Jusqu’à présent, nous avons utilisé Python exclusivement « en mode texte ». Cela nous a permis d’apprendre les rudiments du langage et de débuter le projet final dans une version simple mais fonctionnelle. Il est temps à présent de donner une allure plus moderne et plus ergonomique à nos programmes en utilisant une interface graphique.

Ce qui va suivre ne constitue qu'une première approche, car ce domaine des interfaces graphiques peut très vite faire intervenir des notions de programmation assez complexe, en particulier ce qu'on appelle la Programmation Orientée Objet (POO). Nous ne ferons ici qu'effleurer en surface la POO pour construire des programmes simples. Dans le cadre de cette approche, nous nous limiterons à Tkinter. D'autres systèmes d'interfaces graphiques, plus ou moins élaborés existent.

Premier contact

Dans l'exemple qui suit, nous allons créer une fenêtre très simple, et y ajouter deux composants graphiques (widgets) typiques : un bout de texte (ou label) et un bouton (ou button).

Nous utiliserons IPython pour illustrer nos exemples : l'aspect interactif favorisant la découverte des nouvelles notions. Pour la réalisation d'un gros projet, il peut être préféreable d'utiliser un véritable environnement de développement cependant.

In [ ]:
from tkinter import *
In [ ]:
fen1 = Tk()
tex1 = Label(fen1, text='Bonjour tout le monde !', fg='red')
tex1.pack()
bou1 = Button(fen1, text='Quitter', command = fen1.destroy)
bou1.pack()
fen1.mainloop()

Explications

La première ligne de notre exemple :

from tkinter import *

consiste à importer toutes les classes contenues dans le module tkinter. Nous devrons de plus en plus souvent parler de ces classes.

À la deuxième ligne de notre exemple :

fen1 = Tk()

nous utilisons l'une des classes du module tkinter, la classe Tk(), et nous en créons une instance, à savoir la fenêtre fen1.

Cette instruction d'instanciation ressemble à une simple affectation de variable. Comprenons bien cependant qu'il se passe ici deux choses à la fois :

  • la création d'un nouvel objet, (lequel peut être complexe et donc occuper un espace mémoire considérable)
  • l'affectation d'une variable qui va désormais servir de référence pour manipuler l'objet

A la troisième ligne :

*tex1 = Label(fen1, text='Bonjour tout le monde !', fg='red')

nous créons un autre objet (un widget), cette fois à partir de la classe Label(). Comme son nom l'indique, cette classe définit toutes sortes d'étiquettes. En fait, il s'agit tout simplement de fragments de texte quelconques, utilisables pour afficher des informations et des messages divers à l'intérieur d'une fenêtre. Nous fournissons un certain nombre d'arguments dans des parenthèses.:

Le premier argument transmis (fen1), indique que le nouveau widget que nous sommes en train de créer sera contenu dans un autre widget préexistant, que nous désignons comme le parent (On pourra dire aussi que l'objet tex1 est un enfant de l'objet fen1).

Les deux arguments suivants servent à préciser la forme exacte que doit prendre notre widget. Ce sont en effet deux options de création, chacune fournie sous la forme d'une chaîne de caractères : d'abord le texte de l'étiquette, ensuite sa couleur d'avant-plan (ou foreground, en abrégé fg). Ainsi le texte que nous voulons afficher est bien défini, et il doit apparaître coloré en rouge. Nous pourrions encore préciser bien d'autres caractéristiques : la police à utiliser, ou la couleur d'arrière-plan, par exemple.

À la quatrième ligne de notre exemple : tex1.pack() nous activons la méthode pack(). Nous avons déjà rencontré ce terme de méthode (à propos des listes, notamment). Une méthode est une fonction intégrée à un objet.

Un objet informatique est en fait un morceau de programme contenant toujours : Un certain nombre de données (numériques ou autres), contenues dans des variables de types divers : on les appelle les attributs (ou les propriétés) de l'objet. Un certain nombre de procédures ou de fonctions (qui sont donc des algorithmes) : on les appelle les méthodes de l'objet.

La méthode pack() permet de disposer les widgets dans la fenêtre parent, les uns en dessous des autres. Pour faire simple, elle permet de disposer les différents objets graphiques dans la fenêtre.

A la cinquième ligne :

bou1 = Button(fen1, text='Quitter', command = fen1.destroy)

nous créons notre second widget enfant : un bouton. Comme nous l'avons fait pour le widget précédent, nous appelons la classe Button() en fournissant entre parenthèses un certain nombre d'arguments. Étant donné qu'il s'agit cette fois d'un objet interactif, nous devons préciser avec l'option command ce qui devra se passer lorsque l'utilisateur effectuera un clic sur le bouton. Dans ce cas précis, nous actionnerons la méthode destroy associée à l'objet fen1, ce qui devrait provoquer l'effacement de la fenêtre.

La sixième ligne utilise la méthode pack() pour adapter la géométrie de la fenêtre au nouveau bouton que nous venons d'y intégrer.

La septième ligne :

fen1.mainloop()

est très importante, parce que c'est elle qui provoque le démarrage du réceptionnaire d'événements associé à la fenêtre. Cette instruction est nécessaire pour que votre application soit « à l'affût » des clics de souris, des pressions exercées sur les touches du clavier, etc. C'est donc cette instruction qui « la met en marche », en quelque sorte. Comme son nom l'indique (mainloop), il s'agit d'une méthode de l'objet fen1, qui active une boucle de programme, laquelle « tournera » en permanence en tâche de fond, dans l'attente de messages émis par le système d'exploitation de l'ordinateur. Celui-ci interroge en effet sans cesse son environnement, notamment au niveau des périphériques d'entrée (souris, clavier, etc.). Lorsqu'un événement quelconque est détecté, divers messages décrivant cet événement sont expédiés aux programmes qui souhaitent en être avertis. Voyons cela un peu plus en détail.

Pour vous convaincre de l'utilité de la mainloop, recopiez dans la cellule ci-dessous le programme précédent en retirant la dernière ligne. Que se passe t-il ?

In [ ]:
# Recopiez votre programme ici

Rien ne se passe ? Pourtant, des objets ont été créés et disposés dans la fenêtre !

C'est normal. Validez à présent la cellule ci-dessous :

In [ ]:
mainloop()

Votre application prend vie grâce à la mainloop. Elle est en mesure de réagir à vos actions.

Programmes pilotés par événements

Dans les programmes que vous avez réalisé jusqu'à présent, l'éxécution se déroulait de manièrre linéaire du début à la fin. Souvent, vous commencez par initialiser des variables, puis vous exécutez une ou plusieurs actions dans un ordre bien défini et enfin, vous affichez le résultat de votre travail.

Dans le cas d’un programme qui utilise une interface graphique, par contre, l’organisation interne est différente. On dit d’un tel programme qu’il est piloté par les événements. Après sa phase d’initialisation, un programme de ce type se met en quelque sorte « en attente », et passe la main à un autre logiciel, lequel est plus ou moins intimement intégré au système d’exploitation de l’ordinateur et « tourne » en permanence.

Ce réceptionnaire d’événements scrute sans cesse tous les périphériques (clavier, souris, etc.) et réagit immédiatement lorsqu’un événement y est détecté. Un tel événement peut être une action quelconque de l’utilisateur : déplacement de la souris, appui sur une touche, etc., mais aussi un événement externe ou un automatisme (top d’horloge, par ex.) Lorsqu’il détecte un événement, le réceptionnaire envoie un message spécifique au programme, lequel doit être conçu pour réagir en conséquence. Dans le cas d’un programme avec interface graphique, l’ordre dans lequel les fonctions sont appelées n’est plus inscrit nulle part dans le programme. Ce sont les événements qui pilotent !

A vous de jouer

En vous aidant du programme donné au début de ce classeur, réalisez une application d'apparence semblable à ceci :

In [ ]:
from tkinter import *

# Ecrivez votre code ici
In [ ]: