Comment gérer l'événement de fermeture de fenêtre (l'utilisateur clique sur le bouton «X») dans un programme Python Tkinter?
131
Tkinter prend en charge un mécanisme appelé gestionnaires de protocole . Ici, le terme protocole fait référence à l'interaction entre l'application et le gestionnaire de fenêtres. Le protocole le plus couramment utilisé est appelé WM_DELETE_WINDOW
et sert à définir ce qui se passe lorsque l'utilisateur ferme explicitement une fenêtre à l'aide du gestionnaire de fenêtres.
Vous pouvez utiliser la protocol
méthode pour installer un gestionnaire pour ce protocole (le widget doit être un widget Tk
ou Toplevel
):
Voici un exemple concret:
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
Tkinter
je n'avais pas de boîte de message de sous-module. J'ai utiliséimport tkMessageBox as messagebox
Matt a montré une modification classique du bouton de fermeture.
L'autre est que le bouton de fermeture minimise la fenêtre.
Vous pouvez reproduire ce comportement en ayant la méthode iconify
comme deuxième argument de la méthode de protocole .
Voici un exemple fonctionnel, testé sur Windows 7 et 10:
Dans cet exemple, nous donnons à l'utilisateur deux nouvelles options de sortie:
le classique Fichier → Quitter, ainsi que le Escbouton.
la source
En fonction de l'activité de Tkinter, et en particulier lors de l'utilisation de Tkinter.after, arrêter cette activité avec
destroy()
- même en utilisant protocol (), un bouton, etc. - perturbera cette activité (erreur "lors de l'exécution") plutôt que de simplement la terminer . La meilleure solution dans presque tous les cas est d'utiliser un drapeau. Voici un exemple simple et idiot de comment l'utiliser (même si je suis certain que la plupart d'entre vous n'en ont pas besoin! :)Cela termine bien l'activité graphique. Il vous suffit de vérifier
running
au (x) bon (s) endroit (s).la source
Je tiens à remercier la réponse d'Apostolos d'avoir porté cela à mon attention. Voici un exemple beaucoup plus détaillé pour Python 3 en 2019, avec une description plus claire et un exemple de code.
Méfiez-vous du fait que
destroy()
(ou ne pas avoir de gestionnaire de fermeture de fenêtre personnalisé du tout) détruira la fenêtre et tous ses rappels en cours d'exécution instantanément lorsque l'utilisateur la ferme.Cela peut être mauvais pour vous, en fonction de votre activité Tkinter actuelle, et en particulier lors de l'utilisation
tkinter.after
(rappels périodiques). Vous pourriez utiliser un callback qui traite certaines données et écrit sur le disque ... dans ce cas, vous voulez évidemment que l'écriture des données se termine sans être brusquement tuée.La meilleure solution pour cela est d'utiliser un drapeau. Ainsi, lorsque l'utilisateur demande la fermeture de la fenêtre, vous marquez cela comme un indicateur, puis vous y réagissez.
(Remarque: je conçois normalement les interfaces graphiques comme des classes bien encapsulées et des threads de travail séparés, et je n'utilise certainement pas "global" (j'utilise des variables d'instance de classe à la place), mais cela est censé être un exemple simple et dépouillé pour démontrer comment Tk tue brusquement vos rappels périodiques lorsque l'utilisateur ferme la fenêtre ...)
Ce code vous montrera que le
WM_DELETE_WINDOW
gestionnaire s'exécute même lorsque notre customperiodic_call()
est occupé au milieu du travail / des boucles!Nous utilisons des
.after()
valeurs assez exagérées : 500 millisecondes. Ceci est simplement destiné à vous permettre de voir très facilement la différence entre la fermeture pendant que l'appel périodique est occupé, ou non ... Si vous fermez pendant que les numéros sont mis à jour, vous verrez queWM_DELETE_WINDOW
cela s'est produit pendant votre appel périodique "était traitement occupé: Vrai ". Si vous fermez pendant que les numéros sont en pause (ce qui signifie que le rappel périodique n'est pas en cours de traitement à ce moment-là), vous voyez que la fermeture s'est produite alors qu'il n'est "pas occupé".Dans le monde réel, vous
.after()
utiliseriez quelque chose comme 30 à 100 millisecondes pour avoir une interface graphique réactive. Ceci est juste une démonstration pour vous aider à comprendre comment vous protéger contre le comportement par défaut de Tk "interrompre instantanément tout travail lors de la fermeture".En résumé: faites en
WM_DELETE_WINDOW
sorte que le gestionnaire définisse un indicateur, puis vérifiez cet indicateur périodiquement et manuellement dans.destroy()
la fenêtre lorsqu'elle est en sécurité (lorsque votre application a terminé son travail).PS: Vous pouvez également utiliser
WM_DELETE_WINDOW
pour demander à l'utilisateur s'il veut VRAIMENT fermer la fenêtre; et s'ils répondent non, vous ne définissez pas le drapeau. C'est très simple. Il vous suffit d'afficher une boîte de message dans votreWM_DELETE_WINDOW
et de définir l'indicateur en fonction de la réponse de l'utilisateur.la source
Essayez la version simple:
Ou si vous souhaitez ajouter plus de commandes:
la source
la source