Tkinter
Tkinter (Tk interface) est un module intégré à la bibliothèque standard de Python, permettant de créer des interfaces graphiques :
- des fenêtres,
- des widgets (boutons, zones de texte, cases à cocher, …),
- des évènements (clavier, souris, …).
Tkinter est disponible sur Windows et la plupart des systèmes Unix : les interfaces crées avec Tkinter sont donc portables.
Les noms de Tkinter sont disponibles dans le module tkinter
.
Documentation complète de Tkinter : http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html
On se propose de réaliser une petite calculatrice …
Les concepts d’une interface graphique
Première interface
# Import des noms du module from tkinter import * # Création d'un objet "fenêtre" fenetre = Tk() # Titre (Label) titre = Label(fenetre, text = "L'informatique, c'est fantastique !") # Affichage du titre titre.pack() # Ajout des autres widgets # ......................... # Démarrage de la boucle Tkinter (à placer à la fin !!!) fenetre.mainloop()
Remarque : avant Python 3, le module s’appelait
Tkinter
(avec un T majuscule)
Les objets d’une interface graphique
Les fenêtres
La fenêtre principale d’une application Tkinter se construit avec le constructeur Tk()
.
Par défaut, elle porte le nom
'tk'
et prend la dimension de ce qu’elle contient.
On peut modifier son apparence en passant certaines options lors de sa construction, ou bien après :
fenetre.title("Calculatrice ISN") fenetre.minsize(300,200)
Les widgets
Les éléments graphiques qui apparaissent dans une fenêtre sont nommés widgets. Certains sont statiques (affichage simple) et d’autres permettent une interaction avec l’utilisateur.
Syntaxe commune :
Nom_du_widget(parent, [autres arguments])
Tout widget doit être « attaché » à une fenêtre : lors de sa construction, cela correspond à l’argument parent.
Cadre
Un cadre (frame) est un conteneur pour d’autres widgets. Il permet de réaliser des groupes cohérents de widgets.
L’usage veut que l’on place au moins un un cadre dans la fenêtre de base, pour contenir les widgets de l’application :
cadre = Frame(fenetre) cadre.pack()
Label
Les labels permettent d’afficher du texte dans une fenêtre. Le constructeur Label()
accepte de nombreux arguments pour la mise en forme du texte. Mais on peut modifier les options de l’objet après sa construction grâce à la méthode .configure()
.
Par exemple :
titre.configure(fg = 'red')
Entrée
Une entrée permet à l’utilisateur de saisir un texte sur une seule ligne.
Le constructeur Entry()
accepte de nombreux arguments (options d’apparence, …) dont le plus important est la variable Tkinter, objet de type StringVar
, permettant de lier de façon dynamique le contenu de l’entrée au reste du programme.
# Saisie de l'expression mathématique : Entrée (Entry) expression = StringVar() expression.set("6*7") # texte par défaut affiché dans l'entrée entree = Entry(cadre, textvariable = expression, width = 30) entree.pack()
Pour bien comprendre le comportement d’une variable Tkinter, il suffit de l’utiliser dans un autre widget (ci-dessous avec un Label
):
# Résultat du calcul sortie = Label(cadre, textvariable = expression) sortie.pack()
Mais pour faire une calculatrice, il faut évaluer l’expression saisie. Le label de sortie ne doit donc pas afficher l’expression mathématique, mais une autre variable contenant le résultat.
resultat = StringVar() sortie = Label(cadre, textvariable = resultat) sortie.pack()
Mais par défaut, une StringVar
contient ""
. Il faut donc effectuer une opération de calcul, par exemple à travers une fonction :
def calculer(): resultat.set(eval(expression.get()))
Remarque : les méthodes
.get()
et.set()
permettent respectivement d’obtenir la valeur de la variable et d’affecter une nouvelle valeur à la variable.
Il faut à présent trouver un moyen de lancer cette fonction …
Bouton
Les boutons permettent à l’utilisateur d’exécuter une action. Le constructeur Button()
attend donc un argument command
de type fonction.
# Bouton pour exécuter les calculs bouton = Button(cadre, text = "Calculer", command = calculer) bouton.pack()
Après un appui sur le bouton :
L’inconvénient de la fonction calculer()
, c’est qu’elle provoque une erreur dans les cas où l’expression n’est pas évaluable. Il faut donc prévoir cette éventualité :
def calculer(): try: resultat.set(eval(expression.get())) except: pass
On peut également créer un bouton permettant de quitter l’application :
# Bouton pour quitter l'application bouton_quitter = Button(cadre, text = "Quitter", command = fenetre.quit) bouton_quitter.pack()
Bouton radio
Un bouton radio n’est en principe jamais seul : quand l’utilisateur doit faire un choix unique parmi plusieurs possibilités, on utilise typiquement un groupe de boutons radio.
Par exemple, si nous souhaitons contrôler le mode d’affichage du résultat :
# Boutons radio pour sélection du mode d'affichage mode = StringVar() bouton1 = Radiobutton(cadre, text="normal", variable=mode, value="n", command = calculer) bouton2 = Radiobutton(cadre, text="scientifique", variable=mode, value="s", command = calculer) bouton1.pack() bouton2.pack() bouton1.select()
Case à cocher
Liste
Arrangement des widgets
Jusqu’à présent, tous les widgets se sont placés les uns au dessous des autres, dans l’ordre de leur création, et centrés horizontalement dans la fenêtre.
Il existe différentes méthodes pour gérer les emplacements des widgets dans une fenêtre :
- méthode
.place()
: positionnement absolu (à partir de coordonnées) - méthode
.pack()
: un gestionnaire simple d’empilement de widgets - méthode
.grid()
: arrangement des widgets dans une grille (ou un « tableau »)
Pack
Un bon tutoriel pour pack : https://www.pythontutorial.net/tkinter/tkinter-pack/
Grid
La méthode .grid()
permet d’arranger les widgets dans un « tableau », composé de lignes (rows), de colonnes (columns).
Un widget peut occuper plusieurs lignes et/ou plusieurs colonnes adjacentes (rowspan, columnspan).
Il peut être placé dans différentes positions (sticky ; N : nord, E : est, S : sud, W : ouest) dans sa cellule : sticky=NE (haut-droite), SE (bas-droite), SW (bas-gauche), …
Exemples :
titre.grid(row=0, column=0, columnspan=4) entree.grid(row = 1, column = 0, columnspan = 3, sticky = W+E) ...
Activité :
- Réaliser l’arrangement de tous les widgets de la calculatrice de la manière suivante :
- En consultant plus attentivement la documentation complète de la méthode .grid(), améliorer encore le l’affichage et le comportement des widgets (la zone de saisie doit s’ajuster lorsque la taille de la fenêtre est modifiée !).
Documentation complète de grid()
Les événements
Fonctionnalités avancées
Surveillance d’une variable Tkinter
Il peut être particulièrement intéressant d’exécuter une action dès qu’une variable Tkinter a été modifiée. Pour cela, il faut lui attacher un observateur à l’aide de la méthode
expression.trace("w", calculer)
Attention : la fonction calculer()
sera appelée avec 3 arguments (pas exploités ici). Il faut donc les prévoir dans sa définition :
def calculer(*args): ...
Passer des arguments à une commande
Lorsqu’une commande (widgets Button
, RadioButton
, …), doit comporter un ou plusieurs arguments, on utilise une fonction lambda :
bouton1 = Radiobutton(cadre, text="normal", variable = mode, value = "n", command = lambda v = item: calculer(v))
Fermer correctement une application Tkinter
def quitter(): fenetre.destroy() fenetre.protocol('WM_DELETE_WINDOW', quitter)
Structure de base d’un programme Tkinter
from tkinter import * ################################################################################### # Classe définissant l'objet représentant la fenêtre principale de l'application ################################################################################### class Application(Tk): def __init__(self): Tk.__init__(self) self.title("Application du tonnerre !") # Le titre de la fenêtre self.minsize(1550,850) #taille de fenêtre # Une méthode séparée pour construire le contenu de la fenêtre self.createWidgets() # Méthode de création des widgets def createWidgets(self): self.grid() # Choix du mode d'arrangement # Création des widgets # ........... # ........... # ........... # Un bouton pour quitter l'application self.quitButton = Button(self, text = "Quitter", command = self.destroy) self.quitButton.grid() # D'autres méthodes .... # ...................... app = Application() app.mainloop()
Autres exemples
Zone de dessin interactive
from tkinter import * ################################################################################### # Classe de base pour les objets à dessiner ################################################################################### class Objet(): def __init__(self, canvas): self.canvas = canvas class Rectangle(Objet): def __init__(self, canvas, x0, y0, w, h, bg0, bg1): super().__init__(canvas) # On met l'objet dans la liste des objets du canvas self.canvas.objets.append(self) # Paramètres géométriques self.x0 = x0 self.y0 = y0 self.w = w self.h = h # Couleurs self.bg0 = bg0 self.bg1 = bg1 self.bg = self.bg0 # Couleur courante # Point cliqué self.dx = self.dy = None # On dessine l'objet self.draw() def est_dedans(self, x, y): """ Renvoie True si le point (x,y) est à l'intérieur de l'objet """ return self.x0 < x < self.x0 + self.w and self.y0 < y < self.y0 + self.h def mousemove(self, event): """ Actions réalisées quand la souris est déplacée """ x, y = event.x, event.y # Changement de couleur if self.est_dedans(x, y): self.bg = self.bg1 else: self.bg = self.bg0 # Drag if self.dx is not None: # drag en cours self.x0 = x - self.dx self.y0 = y - self.dy self.draw() def mousedown(self, event): """ Actions réalisées quand le bouton de la souris est appuyé """ x, y = event.x, event.y # Début du drag : on mémorise la position relative du curseur de la souris if self.est_dedans(x, y): self.dx = event.x - self.x0 self.dy = event.y - self.y0 def mouseup(self, event): """ Actions réalisées quand le bouton de la souris est relâché """ x, y = event.x, event.y # Fin du drag self.dx = self.dy = None def draw(self): if hasattr(self, 'id'): # l'objet a déja été dessiné self.canvas.delete(self.id) self.id = self.canvas.create_rectangle((self.x0, self.y0), (self.x0 + self.w, self.y0 + self.h), fill = self.bg) ################################################################################### # Classe définissant le canvas où sont dessinés les objets ################################################################################### class Dessin(Canvas): def __init__(self, fen, width, height, bg): super().__init__(fen, width = width, height=height, bg=bg) self.objets = [] # Liaisons des événements à des méthodes self.bind('<Motion>', self.mousemove) self.bind('<Button-1>', self.mousedown) self.bind('<ButtonRelease-1>', self.mouseup) def draw(self): for o in self.objets: o.draw() def mousemove(self, event): for o in self.objets: o.mousemove(event) def mousedown(self, event): for o in self.objets: o.mousedown(event) def mouseup(self, event): for o in self.objets: o.mouseup(event) ################################################################################### # Classe définissant l'objet représentant la fenêtre principale de l'application ################################################################################### class Application(Tk): def __init__(self, w, h): super().__init__() self.title("Faites glisser les figures") # Le titre de la fenêtre self.w = w self.h = h self.minsize(self.w, self.h) # taille de fenêtre self.geometry(f"{self.w}x{self.h}") self.update() # Une méthode séparée pour construire le contenu de la fenêtre self.createWidgets() def get_size(self): return self.winfo_width(), self.winfo_height() # Méthode de création des widgets def createWidgets(self): self.grid() # Choix du mode d'arrangement # Création des widgets self.dessin = Dessin(self, width = self.winfo_width(), height = self.winfo_height()-30, bg = "ivory") a = Rectangle(self.dessin, 10, 20, 100, 200, "white", "blue") b = Rectangle(self.dessin, 120, 50, 160, 180, "green", "red") self.dessin.grid() # Un bouton pour quitter l'application self.quitButton = Button(self, text = "Quitter", command = self.destroy) self.quitButton.grid() app = Application(600, 400) app.mainloop()
Excellente présentation !
Bonjour,
Merci pour cet excellent cours. Il me permet de comprendre Tkinder et de l’utiliser pour mon propre projet. Un grand merci.
Par contre, j’ai passé pas mal de temps à réaliser qu’après
« resultat = StringVar()
sortie = Label(cadre, textvariable = resultat) »
il fallait ajouter :
sortie.pack()
Je le note au cas où d’autres étudiants seraient passés à coté également.
Merci encore pour ce cours !
Mathilde
Merci pour la remarque, j’ai ajouté la ligne.
Très bon cours, j’ai beaucoup appris sur tkinter Merci infiniment
Bonjour,
Actuellement étudiant en deuxième année de Master Ingénierie et Ergonomie de l’Activité Physique, je travaille sur la conception d’un miroir connecté permettant d’afficher différentes informations.
J’utilise un Raspberry Pi 3 et le langage python pour réaliser l’interface graphique.
Je souhaiterais savoir comment afficher un widget météo (html) sur une interface réalisé en python. Est ce que Tkinter permet de réaliser ceci? Dois-je utiliser une autre bibliothèque?
Je vous remercie par avance pour votre réponse.
Romain
Bonjour
Je ne suis pas sûr de bien comprendre …
Si vous réalisez un serveur web (client+serveur), pour afficher un widget sur un page html, il ne faut pas utiliser tkinter, car ce dernier est une interface graphique pour faire des applications « locales ».
Je vous conseille plutôt de générer du code html5 avec python/flask (coté serveur)
Ou bien d’utiliser du javascript.
Si vous réalisez une application « locale », tkinter peut convenir, mais tout dépend de ce que vous voulez afficher dans votre widget.
CF