Module 3Appliquer la POO en Python
Chapitre 9 sur 10
Gérer les interfaces graphiques avec Tkinter
Créer des fenêtres, des widgets et des événements avec Tkinter, en orienté objet.
- ≈ 125 min
- 5 leçons
- 2 exercices
- TP
Module 3Appliquer la POO en Python
Chapitre 9 sur 10
Créer des fenêtres, des widgets et des événements avec Tkinter, en orienté objet.
Objectifs
Avec ce chapitre, tu sauras…
Label, Button, Entry, Text, Listbox, Checkbutton, Radiobutton ;pack, grid et place ;Jusqu'ici, tes programmes communiquaient par le terminal (input, print). Une interface graphique (GUI, Graphical User Interface) offre des fenêtres, des boutons et des champs de saisie. Tkinter est la bibliothèque graphique fournie avec Python : aucune installation n'est nécessaire.
Leçon 1
import tkinter as tk
fenetre = tk.Tk() # la fenêtre principale (un objet)
fenetre.title("Ma première fenêtre")
fenetre.geometry("320x120") # largeur x hauteur
message = tk.Label(fenetre, text="Bonjour M103 !", font=("Arial", 16))
message.pack(pady=20) # placer le widget
fenetre.mainloop() # boucle principale : attend les événementsLeçon 2
| Widget | Rôle | Exemple |
|---|---|---|
Label | texte ou image non modifiable | tk.Label(f, text="Nom :") |
Button | bouton cliquable | tk.Button(f, text="OK", command=valider) |
Entry | champ de saisie sur une ligne | champ = tk.Entry(f) ; champ.get() |
Text | zone de texte sur plusieurs lignes | tk.Text(f, height=5) |
Listbox | liste d'éléments sélectionnables | liste.insert(tk.END, "Sara") |
Checkbutton | case à cocher | avec une tk.BooleanVar() |
Radiobutton | bouton radio | plusieurs partagent une tk.StringVar() |
Frame | cadre qui regroupe d'autres widgets | cadre = tk.Frame(f) |
messagebox | boîte de dialogue | messagebox.showinfo("Titre", "Texte") |
Leçon 3
| Gestionnaire | Principe | Usage |
|---|---|---|
pack() | empile les widgets (haut, bas, gauche, droite) | interfaces simples |
grid(row=, column=) | place les widgets dans une grille de lignes et colonnes | formulaires |
place(x=, y=) | position exacte en pixels | rarement : ne s'adapte pas |
Leçon 4
| Écriture | Effet |
|---|---|
command=fonction | appelle la fonction au clic (sans parenthèses !) |
widget.bind("<Return>", f) | appelle f(event) quand on appuie sur Entrée |
"<Button-1>", "<KeyPress>", "<Double-Button-1>" | clic gauche, touche, double-clic |
Leçon 5
1Tkinter est…
2La méthode qui lance la boucle des événements est…
3Pour lire le texte d'un Entry :
4Le gestionnaire idéal pour un formulaire est…
5Pour associer une fonction au clic d'un bouton :
6Dans un même conteneur, on peut mélanger pack et grid…
L'essentiel du chapitre
mainloop().tk.Tk() (fenêtre), Label, Button(command=f), Entry (get()), Text, Listbox, Checkbutton, Radiobutton, Frame, messagebox.pack(), grid(row, column) (formulaires), place() ; un seul gestionnaire par conteneur.command=fonction (sans parenthèses), bind("<Return>", f).class App(tk.Tk)) qui utilise des classes métier séparées.Mélanger pack et grid
Le module tkinter.ttk propose les mêmes widgets avec une apparence plus moderne, proche du système : from tkinter import ttk puis ttk.Button(...), ttk.Entry(...), plus quelques widgets en plus (ttk.Combobox, ttk.Treeview pour les tableaux).
import tkinter as tk
from tkinter import messagebox
def saluer(event=None): # event : pour l'appel par une touche
nom = champ.get().strip()
if not nom:
messagebox.showwarning("Attention", "Entre ton prénom.")
return
resultat.config(text=f"Bonjour {nom} !")
fenetre = tk.Tk()
fenetre.title("Salutation")
tk.Label(fenetre, text="Prénom :").grid(row=0, column=0, padx=5, pady=5)
champ = tk.Entry(fenetre)
champ.grid(row=0, column=1, padx=5, pady=5)
tk.Button(fenetre, text="Saluer", command=saluer).grid(row=1, column=0, columnspan=2)
resultat = tk.Label(fenetre, text="")
resultat.grid(row=2, column=0, columnspan=2, pady=5)
champ.bind("<Return>", saluer) # la touche Entrée déclenche saluer()
fenetre.mainloop()command=saluer() exécute la fonction tout de suite, au moment de la création du bouton, et passe son résultat (None) au bouton. Il faut passer la fonction elle-même : command=saluer. Pour transmettre un argument : command=lambda: saluer("Sara").
Dans une vraie application, on écrit l'interface sous forme de classe : les widgets deviennent des attributs, les actions des méthodes, et l'interface utilise les classes métier (le Compte du chapitre 7).
import tkinter as tk
from tkinter import messagebox
class SoldeInsuffisant(Exception):
pass
class Compte:
def __init__(self, numero, solde=0):
self.numero = numero
self.solde = solde
def deposer(self, montant):
if montant <= 0:
raise ValueError("Le montant doit être positif.")
self.solde += montant
def retirer(self, montant):
if montant <= 0:
raise ValueError("Le montant doit être positif.")
if montant > self.solde:
raise SoldeInsuffisant(f"Solde disponible : {self.solde:.2f} DH")
self.solde -= montant
class ApplicationBanque(tk.Tk): # hérite de la fenêtre Tk
def __init__(self, compte):
super().__init__()
self.compte = compte # composition : l'interface utilise un Compte
self.title(f"Compte {compte.numero}")
self.creer_widgets()
self.actualiser()
def creer_widgets(self):
self.label_solde = tk.Label(self, font=("Arial", 16))
self.label_solde.grid(row=0, column=0, columnspan=2, pady=10)
tk.Label(self, text="Montant :").grid(row=1, column=0, padx=5)
self.champ = tk.Entry(self)
self.champ.grid(row=1, column=1, padx=5)
tk.Button(self, text="Déposer", command=self.deposer).grid(row=2, column=0, pady=10)
tk.Button(self, text="Retirer", command=self.retirer).grid(row=2, column=1, pady=10)
def lire_montant(self):
return float(self.champ.get().replace(",", "."))
def deposer(self):
self.operation(self.compte.deposer)
def retirer(self):
self.operation(self.compte.retirer)
def operation(self, methode):
try:
methode(self.lire_montant())
except ValueError as e:
messagebox.showerror("Erreur", str(e) or "Montant invalide.")
except SoldeInsuffisant as e:
messagebox.showwarning("Refusé", str(e))
else:
self.champ.delete(0, tk.END)
self.actualiser()
def actualiser(self):
self.label_solde.config(text=f"Solde : {self.compte.solde:.2f} DH")
if __name__ == "__main__":
ApplicationBanque(Compte("MA001", 1000)).mainloop()Les classes Compte et SoldeInsuffisant ne connaissent rien de Tkinter : on pourrait remplacer l'interface graphique par une page web ou une application mobile sans les modifier. Cette séparation entre logique métier et interface est un principe fondamental du développement professionnel (on la retrouve dans l'architecture MVC).
Crée une classe ApplicationStagiaires(tk.Tk) avec un champ « Nom », un bouton « Ajouter » qui ajoute le nom dans une Listbox, un bouton « Supprimer » qui supprime le nom sélectionné, et un label qui affiche le nombre de stagiaires.
Crée une application Tkinter qui convertit des dirhams en euros (1 EUR = 10,8 DH) : un champ de saisie, un bouton « Convertir », un label pour le résultat, et un message d'erreur si la saisie n'est pas un nombre. La touche Entrée doit aussi lancer la conversion.
Dans un même conteneur (une fenêtre ou un Frame), utilise un seul gestionnaire : mélanger pack() et grid() fige l'application. Tu peux en revanche utiliser grid() dans un cadre et pack() dans un autre.