Come posso gestire la finestra evento close in Tkinter?

voti
77

Come posso gestire la finestra vicino evento (utente fa clic sul pulsante 'X') in un programma Python Tkinter?

È pubblicato 21/09/2008 alle 13:46
fonte dall'utente
In altre lingue...                            


6 risposte

voti
101

Tkinter supporta un meccanismo chiamato gestori di protocollo . Qui, il termine protocollo riferisce all'interazione tra l'applicazione e il gestore delle finestre. Il protocollo più comunemente usato è chiamato WM_DELETE_WINDOW, ed è utilizzato per definire cosa accade quando l'utente chiude esplicitamente una finestra utilizzando window manager.

È possibile utilizzare il protocolmetodo per installare un gestore per questo protocollo (il widget deve essere una Tko Toplevelwidget di):

Qui si ha un esempio concreto:

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()
Risposto il 21/09/2008 a 13:51
fonte dall'utente

voti
-14

Utilizzare il CloseEvent

def closeEvent(self, event):
# code to be executed
Risposto il 28/05/2011 a 19:22
fonte dall'utente

voti
13

Matt ha mostrato una modifica classica del pulsante di chiusura.
L'altro è quello di avere il pulsante di chiusura minimizzare la finestra.
È possibile riprodurre questo comportamento da avere il iconify metodo di
essere il protocollo secondo argomento del metodo.

Ecco un esempio di lavoro, testato su Windows 7:

# Python 3
import tkinter
import tkinter.scrolledtext as scrolledtext

class GUI(object):

    def __init__(self):
        root = self.root = tkinter.Tk()
        root.title('Test')

    # make the top right close button minimize (iconify) the main window
        root.protocol("WM_DELETE_WINDOW", root.iconify)

    # make Esc exit the program
        root.bind('<Escape>', lambda e: root.destroy())

    # create a menu bar with an Exit command
        menubar = tkinter.Menu(root)
        filemenu = tkinter.Menu(menubar, tearoff=0)
        filemenu.add_command(label="Exit", command=root.destroy)
        menubar.add_cascade(label="File", menu=filemenu)
        root.config(menu=menubar)

    # create a Text widget with a Scrollbar attached
        txt = scrolledtext.ScrolledText(root, undo=True)
        txt['font'] = ('consolas', '12')
        txt.pack(expand=True, fill='both')

gui = GUI()
gui.root.mainloop()

In questo esempio diamo all'utente due nuove opzioni di uscita:
il classico menù File -> Exit, e anche il Escpulsante.

Risposto il 11/02/2013 a 17:46
fonte dall'utente

voti
0

A seconda dell'attività Tkinter, finire esp. quando si utilizza Tkinter.after, fermandosi questa attività con destroy()- anche utilizzando il protocollo (), un pulsante, ecc - disturberà questa attività ( "durante l'esecuzione di" errore) piuttosto che risolverlo. La soluzione migliore in quasi tutti i casi è quello di utilizzare una bandiera. Ecco un semplice, stupido esempio di come utilizzarlo (anche se sono certo che molti di voi non hanno bisogno di esso! :)

from Tkinter import *

def close_window():
  global running
  running = False
  print "Window closed"

root = Tk()
root.protocol("WM_DELETE_WINDOW", close_window)
cv = Canvas(root, width=200, height=200); cv.pack()

running = True;
# This is an endless loop stopped only by setting 'running' to 'False'
while running: 
  for i in range(200): 
    if not running: break
    cv.create_oval(i,i,i+1,i+1); root.update() 

Questo termina l'attività grafica piacevolmente. Hai solo bisogno di controllare runningal posto giusto (s).

Risposto il 12/04/2018 a 16:50
fonte dall'utente

voti
0

Prova la versione semplice:

import tkinter

window = Tk()

closebutton = Button(window, text='X', command=window.destroy)
closebutton.pack()

window.mainloop()

O se si desidera aggiungere altri comandi:

import tkinter

window = Tk()


def close():
    window.destroy()
    #More Functions


closebutton = Button(window, text='X', command=close)
closebutton.pack()

window.mainloop()
Risposto il 24/06/2019 a 06:59
fonte dall'utente

voti
0

Vorrei ringraziare la risposta da Apostolos per aver portato questo alla mia attenzione. Ecco un esempio molto più dettagliata per Python 3 per l'anno 2019, con la descrizione e l'esempio più chiaro di codice.


Attenzione al fatto che destroy()(o non avere un gestore di chiusura finestra personalizzata a tutti) distruggerà la finestra e tutte le sue funzioni di callback in esecuzione immediatamente quando l'utente lo chiude.

Questo può essere un male per voi, a seconda della vostra attività Tkinter corrente, e soprattutto quando si utilizza tkinter.after(callback periodici). Si potrebbe utilizzare un callback che elabora alcuni dati e scrive su disco ... in questo caso, ovviamente desidera che la scrittura dei dati per terminare senza essere bruscamente uccisi.

La soluzione migliore per questo è di usare una bandiera. Così, quando la chiusura della finestra richieste degli utenti, si contrassegna che, come una bandiera, e poi reagire ad esso.

(Nota: normalmente progettare GUI classi ben incapsulati e thread di lavoro separati, e io sicuramente non uso "globale" (io uso le variabili di istanza di classe, invece), ma questo è pensato per essere un semplice, ad esempio stripped-down per dimostrare come Tk uccide improvvisamente i tuoi callback periodici quando l'utente chiude la finestra ...)

from tkinter import *
import time

# Try setting this to False and look at the printed numbers (1 to 10)
# during the work-loop, if you close the window while the periodic_call
# worker is busy working (printing). It will abruptly end the numbers,
# and kill the periodic callback! That's why you should design most
# applications with a safe closing callback as described in this demo.
safe_closing = True

# ---------

busy_processing = False
close_requested = False

def close_window():
    global close_requested
    close_requested = True
    print("User requested close at:", time.time(), "Was busy processing:", busy_processing)

root = Tk()
if safe_closing:
    root.protocol("WM_DELETE_WINDOW", close_window)
lbl = Label(root)
lbl.pack()

def periodic_call():
    global busy_processing

    if not close_requested:
        busy_processing = True
        for i in range(10):
            print((i+1), "of 10")
            time.sleep(0.2)
            lbl["text"] = str(time.time()) # Will error if force-closed.
            root.update() # Force redrawing since we change label multiple times in a row.
        busy_processing = False
        root.after(500, periodic_call)
    else:
        print("Destroying GUI at:", time.time())
        try: # "destroy()" can throw, so you should wrap it like this.
            root.destroy()
        except:
            pass

root.after_idle(periodic_call)
root.mainloop()

Questo codice vi mostrerà che il WM_DELETE_WINDOWgestore gestisce anche mentre nostra abitudine periodic_call()è occupato nel mezzo di lavoro / loop!

Usiamo alcuni piuttosto esagerate .after()valori: 500 millisecondi. Questo è solo lo scopo di rendere molto facile per voi per vedere la differenza tra la chiusura mentre la chiamata periodica è occupato, o no ... Se si chiude mentre i numeri sono l'aggiornamento, si vedrà che l' WM_DELETE_WINDOWaccaduto , mentre la chiamata periodica "è stato elaborando: true". Se si chiude, mentre i numeri sono in pausa (il che significa che la richiamata periodica non elabora in quel momento), si vede che la stretta è accaduto mentre è "non occupato".

Nell'uso del mondo reale, il tuo .after()sarebbe usare qualcosa come 30-100 millisecondi, per avere una GUI reattivo. Questa è solo una dimostrazione per aiutarvi a capire come proteggersi contro l'insolvenza di Tk "interrompere immediatamente tutti i lavori in fase di chiusura" comportamento.

In sintesi: Rendere il WM_DELETE_WINDOWgestore impostato una bandiera, e quindi controllare che la bandiera periodicamente e manualmente .destroy()la finestra quando è sicuro (quando la vostra applicazione è fatto con tutto il lavoro).

PS: È inoltre possibile utilizzare WM_DELETE_WINDOWper chiedere all'utente se davvero vogliono chiudere la finestra; e se rispondono di no, non si imposta il flag. È molto semplice. Basta mostrare un MessageBox nella tua WM_DELETE_WINDOWe imposta il flag in base alla risposta dell'utente.

Risposto il 20/10/2019 a 02:10
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more