logo oujood
🔍

Les fonctions callback Tkinter

Un callback est une fonction qu'on passe à un widget pour qu'il l'appelle plus tard. Bien les organiser dès le départ évite beaucoup de code spaghetti.

OUJOOD.COM

Dans Tkinter, un callback est simplement une fonction qu'on confie à un widget — bouton, champ de saisie, variable de contrôle — pour qu'il l'appelle au moment opportun. Le terme peut sembler technique, mais c'est exactement ce que fait command=ma_fonction : on donne la fonction, Tkinter s'occupe de l'appeler.

Callback simple et portée des variables

La première difficulté avec les callbacks est la portée : la fonction a-t-elle accès aux widgets et variables dont elle a besoin ?

📋 Copier le code

import tkinter as tk

fenetre = tk.Tk()
fenetre.geometry("340x180")

def valider():
    # La fonction accède à champ et label_msg via la portée englobante
    texte = champ.get().strip()
    if texte:
        label_msg.config(text=f"Bonjour, {texte} !", fg="#2e7d32")
    else:
        label_msg.config(text="Veuillez saisir un prénom.", fg="#c62828")

tk.Label(fenetre, text="Votre prénom :").pack(pady=(20, 4))
champ = tk.Entry(fenetre, width=24)
champ.pack()
tk.Button(fenetre, text="Valider", command=valider).pack(pady=8)
label_msg = tk.Label(fenetre, text="")
label_msg.pack()

fenetre.mainloop()

La fonction valider() accède à champ et label_msg via la portée englobante. Cela fonctionne parce que ces variables existent au moment où la fonction est appelée — peu importe qu'elles soient définies après la fonction dans le code source.

Factoriser plusieurs callbacks similaires

Quand plusieurs boutons déclenchent des actions proches, une seule fonction avec paramètre est plus propre que plusieurs fonctions quasi-identiques :

📋 Copier le code

import tkinter as tk
from functools import partial

def changer_theme(couleur_fond, couleur_texte):
    fenetre.configure(bg=couleur_fond)
    label_titre.config(bg=couleur_fond, fg=couleur_texte)
    frame_btns.config(bg=couleur_fond)

fenetre = tk.Tk()
fenetre.geometry("360x160")

label_titre = tk.Label(fenetre, text="Choisissez un thème",
                       font=("Arial", 14), pady=20)
label_titre.pack()

frame_btns = tk.Frame(fenetre)
frame_btns.pack(pady=5)

themes = [
    ("Clair",  "#f5f5f5", "#212121"),
    ("Sombre", "#212121", "#f5f5f5"),
    ("Bleu",   "#1a237e", "#e8eaf6"),
]

for nom, fond, texte in themes:
    tk.Button(
        frame_btns, text=nom,
        command=partial(changer_theme, fond, texte)
    ).pack(side="left", padx=8)

fenetre.mainloop()

partial fige les arguments au moment de la création du bouton. C'est plus lisible que trois lambdas séparés et évite le piège de la closure en boucle mentionné dans la page sur le clic de bouton.

Callback de validation de formulaire

Un pattern très courant : un callback central qui lit plusieurs champs, valide les données et affiche le résultat :

📋 Copier le code

import tkinter as tk

fenetre = tk.Tk()
fenetre.title("Inscription")
fenetre.geometry("380x240")

champs = {}
labels_err = {}

def creer_champ(parent, nom, row, masque=False):
    tk.Label(parent, text=f"{nom} :").grid(row=row, column=0, sticky="e", padx=10, pady=5)
    var = tk.StringVar()
    opts = {"textvariable": var, "width": 22}
    if masque:
        opts["show"] = "•"
    tk.Entry(parent, **opts).grid(row=row, column=1, padx=5)
    err = tk.Label(parent, text="", fg="#c62828", font=("Arial", 9))
    err.grid(row=row, column=2, sticky="w", padx=4)
    champs[nom] = var
    labels_err[nom] = err

def soumettre():
    valide = True
    for nom, var in champs.items():
        if not var.get().strip():
            labels_err[nom].config(text="Requis")
            valide = False
        else:
            labels_err[nom].config(text="✓")
    if valide:
        label_statut.config(text="Inscription réussie !", fg="#2e7d32")

frame = tk.Frame(fenetre)
frame.pack(pady=15)

creer_champ(frame, "Prénom", 0)
creer_champ(frame, "Email", 1)
creer_champ(frame, "Mot de passe", 2, masque=True)

tk.Button(fenetre, text="S'inscrire", command=soumettre).pack(pady=5)
label_statut = tk.Label(fenetre, text="")
label_statut.pack()

fenetre.mainloop()

Le callback soumettre() parcourt tous les champs via les dictionnaires champs et labels_err — pas besoin d'une variable par champ. Cette structure passe bien à l'échelle quand le formulaire grandit.

Pour les interfaces plus complexes, structurer les callbacks dans une classe est l'étape suivante — c'est ce que couvre la page sur l'organisation en classes. Pour les événements clavier et souris au-delà du simple clic, la page sur bind() prend la suite.

Par carabde | Mis à jour le 30 avril 2025