logo oujood
🔍

Ouvrir une fenêtre secondaire Tkinter

Toplevel crée une fenêtre indépendante de la fenêtre principale. Voici comment l'ouvrir, la rendre modale et faire circuler des données entre les deux.

OUJOOD.COM

Dans Tkinter, toute fenêtre supplémentaire se crée avec Toplevel. Elle se comporte comme une fenêtre autonome — titre, widgets, dimensions propres — mais reste liée au cycle de vie de la fenêtre principale : quand la principale se ferme, toutes les Toplevel se ferment aussi.

Fenêtre secondaire simple

📋 Copier le code

import tkinter as tk

fenetre = tk.Tk()
fenetre.title("Fenêtre principale")
fenetre.geometry("360x200")

def ouvrir_secondaire():
    secondaire = tk.Toplevel(fenetre)
    secondaire.title("Fenêtre secondaire")
    secondaire.geometry("300x150")
    tk.Label(secondaire, text="Je suis une fenêtre secondaire",
             font=("Arial", 11)).pack(expand=True)
    tk.Button(secondaire, text="Fermer",
              command=secondaire.destroy).pack(pady=10)

tk.Button(fenetre, text="Ouvrir une fenêtre",
          command=ouvrir_secondaire).pack(expand=True)

fenetre.mainloop()

Chaque appel à ouvrir_secondaire() crée une nouvelle fenêtre indépendante. Si on veut éviter plusieurs instances simultanées, il faut vérifier si une fenêtre existe déjà avant d'en créer une nouvelle — en stockant la référence dans une variable et en vérifiant avec winfo_exists().

Fenêtre modale — bloquer la fenêtre principale

grab_set() capte tous les événements clavier et souris et les dirige vers la Toplevel, rendant la fenêtre principale inaccessible tant que la secondaire est ouverte :

📋 Copier le code

import tkinter as tk

fenetre = tk.Tk()
fenetre.title("Application principale")
fenetre.geometry("380x200")

def confirmer_action():
    modale = tk.Toplevel(fenetre)
    modale.title("Confirmation")
    modale.geometry("300x140")
    modale.resizable(False, False)

    # Centrer par rapport à la fenêtre principale
    x = fenetre.winfo_x() + (fenetre.winfo_width()  - 300) // 2
    y = fenetre.winfo_y() + (fenetre.winfo_height() - 140) // 2
    modale.geometry(f"+{x}+{y}")

    modale.grab_set()        # rend la fenêtre modale
    modale.focus_set()

    tk.Label(modale, text="Voulez-vous vraiment continuer ?",
             font=("Arial", 11), wraplength=260).pack(pady=20)

    frame_btn = tk.Frame(modale)
    frame_btn.pack()

    def oui():
        label_res.config(text="Action confirmée ✓", fg="#2e7d32")
        modale.destroy()

    def non():
        label_res.config(text="Action annulée", fg="#c62828")
        modale.destroy()

    tk.Button(frame_btn, text="Oui", width=8, command=oui).pack(side="left", padx=8)
    tk.Button(frame_btn, text="Non", width=8, command=non).pack(side="left")

    fenetre.wait_window(modale)  # bloque jusqu'à la fermeture de la modale

tk.Button(fenetre, text="Lancer l'action", command=confirmer_action).pack(pady=30)
label_res = tk.Label(fenetre, text="")
label_res.pack()

fenetre.mainloop()

wait_window() suspend l'exécution du code après l'appel jusqu'à ce que la fenêtre soit fermée, tout en laissant la boucle d'événements tourner. C'est ce qui permet d'écrire du code qui s'exécute après la fermeture de la modale, comme si c'était synchrone.

Passer des données depuis la fenêtre secondaire

Pour récupérer une valeur saisie dans une Toplevel, on stocke le résultat dans une variable partagée avant de détruire la fenêtre :

📋 Copier le code

import tkinter as tk

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

def demander_prenom():
    resultat = tk.StringVar()

    dialog = tk.Toplevel(fenetre)
    dialog.title("Saisie")
    dialog.geometry("260x120")
    dialog.grab_set()

    tk.Label(dialog, text="Votre prénom :").pack(pady=(15, 4))
    champ = tk.Entry(dialog, width=22)
    champ.pack()
    champ.focus_set()

    def valider():
        resultat.set(champ.get())
        dialog.destroy()

    tk.Button(dialog, text="OK", command=valider).pack(pady=8)
    dialog.bind("", lambda e: valider())

    fenetre.wait_window(dialog)

    # Ici, dialog est fermé — resultat contient la valeur saisie
    if resultat.get():
        label.config(text=f"Bonjour, {resultat.get()} !")

tk.Button(fenetre, text="Saisir un prénom", command=demander_prenom).pack(pady=30)
label = tk.Label(fenetre, text="")
label.pack()

fenetre.mainloop()

La StringVar sert de canal de communication entre les deux fenêtres. Pour des besoins plus complexes — plusieurs champs, validation — structurer la Toplevel dans une classe est plus maintenable. Voir aussi les dialogues prêts à l'emploi de messagebox et filedialog qui couvrent les cas courants sans Toplevel.

Par carabde | Mis à jour le 30 avril 2025