Wie gehe ich mit dem Fensterschließereignis (Benutzer klickt auf die Schaltfläche 'X') in einem Python Tkinter-Programm um?
130
Tkinter unterstützt einen Mechanismus namens Protokollhandler . Hier bezieht sich der Begriff Protokoll auf die Interaktion zwischen der Anwendung und dem Fenstermanager. Das am häufigsten verwendete Protokoll wird aufgerufen WM_DELETE_WINDOW
und definiert, was passiert, wenn der Benutzer ein Fenster mithilfe des Fenstermanagers explizit schließt.
Mit der protocol
Methode können Sie einen Handler für dieses Protokoll installieren (das Widget muss ein Tk
oder ein Toplevel
Widget sein):
Hier haben Sie ein konkretes Beispiel:
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
es kein Submodul-Meldungsfeld. Ich benutzteimport tkMessageBox as messagebox
Matt hat eine klassische Modifikation des Schließknopfes gezeigt.
Die andere Möglichkeit besteht darin, dass die Schaltfläche zum Schließen das Fenster minimiert.
Sie können dieses Verhalten reproduziert , indem die mit Iconify Methode
der seine Protokoll - Methode der zweite Argument.
Hier ist ein Arbeitsbeispiel, das unter Windows 7 und 10 getestet wurde:
In diesem Beispiel geben wir dem Benutzer zwei neue Exit-Optionen:
die klassische Datei → Beenden und auch die EscSchaltfläche.
quelle
Abhängig von der Tkinter-Aktivität und insbesondere bei Verwendung von Tkinter.after wird das Stoppen dieser Aktivität mit
destroy()
- auch unter Verwendung von protocol (), einer Schaltfläche usw. - diese Aktivität stören (Fehler "während der Ausführung"), anstatt sie nur zu beenden . Die beste Lösung ist in fast allen Fällen die Verwendung einer Flagge. Hier ist ein einfaches, albernes Beispiel für die Verwendung (obwohl ich sicher bin, dass die meisten von Ihnen es nicht brauchen! :)Dies beendet die Grafikaktivität gut. Sie müssen nur
running
an den richtigen Stellen nachsehen.quelle
Ich möchte der Antwort von Apostolos dafür danken, dass er mich darauf aufmerksam gemacht hat. Hier ist ein viel detaillierteres Beispiel für Python 3 im Jahr 2019 mit einer klareren Beschreibung und einem Beispielcode.
Beachten Sie, dass
destroy()
das Fenster und alle laufenden Rückrufe sofort zerstört werden, wenn der Benutzer es schließt (oder überhaupt keinen benutzerdefinierten Handler zum Schließen von Fenstern hat) .Dies kann abhängig von Ihrer aktuellen Tkinter-Aktivität und insbesondere bei der Verwendung
tkinter.after
(regelmäßige Rückrufe) schlecht für Sie sein . Möglicherweise verwenden Sie einen Rückruf, der einige Daten verarbeitet und auf die Festplatte schreibt. In diesem Fall möchten Sie offensichtlich, dass das Schreiben der Daten beendet wird, ohne abrupt beendet zu werden.Die beste Lösung dafür ist die Verwendung eines Flags. Wenn der Benutzer das Schließen des Fensters anfordert, markieren Sie dies als Flag und reagieren darauf.
(Hinweis: Normalerweise entwerfe ich GUIs als gut gekapselte Klassen und separate Arbeitsthreads, und ich verwende definitiv nicht "global" (ich verwende stattdessen Klasseninstanzvariablen), aber dies soll ein einfaches, reduziertes Beispiel sein, um dies zu demonstrieren wie Tk Ihre regelmäßigen Rückrufe abrupt beendet, wenn der Benutzer das Fenster schließt ...)
Dieser Code zeigt Ihnen, dass der
WM_DELETE_WINDOW
Handler auch ausgeführt wird, während unser Benutzerperiodic_call()
mitten in der Arbeit / den Schleifen beschäftigt ist!Wir verwenden einige ziemlich übertriebene
.after()
Werte: 500 Millisekunden. Dies soll es Ihnen nur sehr leicht machen, den Unterschied zwischen dem Schließen während des periodischen Anrufs zu erkennen oder nicht ... Wenn Sie schließen, während die Nummern aktualisiert werden, werden Sie sehen, dass dasWM_DELETE_WINDOW
passiert ist, während Ihr periodischer Anruf "war beschäftigt Verarbeitung: True ". Wenn Sie schließen, während die Nummern angehalten sind (was bedeutet, dass der periodische Rückruf in diesem Moment nicht verarbeitet wird), sehen Sie, dass das Schließen stattgefunden hat, während es "nicht besetzt" ist.In der Praxis
.after()
würden Sie etwa 30 bis 100 Millisekunden verwenden, um eine reaktionsschnelle Benutzeroberfläche zu erhalten. Dies ist nur eine Demonstration, die Ihnen hilft, zu verstehen, wie Sie sich vor dem Standardverhalten von Tk schützen können, "alle Arbeiten beim Schließen sofort unterbrechen".Zusammenfassend: Lassen Sie den
WM_DELETE_WINDOW
Handler ein Flag setzen und überprüfen Sie dieses Flag dann regelmäßig und manuell.destroy()
im Fenster, wenn es sicher ist (wenn Ihre App mit allen Arbeiten fertig ist).PS: Sie können auch verwenden ,
WM_DELETE_WINDOW
um fragen den Benutzer , wenn sie wirklich das Fenster schließen wollen; und wenn sie mit nein antworten, setzen Sie das Flag nicht. Es ist sehr einfach. Sie zeigen einfach eine Nachrichtenbox in Ihrem anWM_DELETE_WINDOW
und setzen das Flag basierend auf der Antwort des Benutzers.quelle
Versuchen Sie die einfache Version:
Oder wenn Sie weitere Befehle hinzufügen möchten:
quelle
quelle