[英]How do I handle the window close event in Tkinter?
如何在 Python Tkinter 程序中處理窗口關閉事件(用戶單擊“X”按鈕)?
Tkinter 支持一種稱為協議處理程序的機制。 這里,術語協議是指應用程序和窗口管理器之間的交互。 最常用的協議稱為WM_DELETE_WINDOW
,用於定義當用戶使用窗口管理器顯式關閉窗口時會發生什么。
您可以使用protocol
方法為此協議安裝處理程序(小部件必須是Tk
或Toplevel
小部件):
這里有一個具體的例子:
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()
Matt 展示了對關閉按鈕的一種經典修改。
另一種是讓關閉按鈕最小化窗口。
您可以通過使用iconify方法重現此行為
是協議方法的第二個參數。
這是一個工作示例,在 Windows 7 和 10 上進行了測試:
# Python 3
import tkinter
import tkinter.scrolledtext as scrolledtext
root = tkinter.Tk()
# 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')
root.mainloop()
在此示例中,我們為用戶提供了兩個新的退出選項:
經典的文件 → 退出,以及Esc按鈕。
取決於 Tkinter 活動,尤其是在使用 Tkinter.after 時,使用destroy()
停止此活動 - 即使使用協議()、按鈕等 - 將干擾此活動(“執行時”錯誤)而不是只是終止它。 幾乎在所有情況下,最好的解決方案是使用標志。 這是一個如何使用它的簡單而愚蠢的示例(盡管我確信你們中的大多數人都不需要它!:)
from Tkinter import *
def close_window():
global running
running = False # turn off while loop
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()
這很好地終止了圖形活動。 您只需要檢查在正確的地方running
。
如果您想更改 x 按鈕的功能或使其無法關閉,請嘗試此操作。
yourwindow.protocol("WM_DELETE_WINDOW", whatever)
然后無視“隨便”是什么意思
def whatever():
# Replace this with your own event for example:
print("oi don't press that button")
您也可以這樣做,以便在關閉該窗口時可以像這樣將其回調
yourwindow.withdraw()
這會隱藏窗口但不會關閉它
yourwindow.deiconify()
這使窗口再次可見
我要感謝 Apostolos 的回答讓我注意到了這一點。 這是 2019 年 Python 3 的更詳細示例,具有更清晰的描述和示例代碼。
destroy()
(或根本沒有自定義窗口關閉處理程序)會在用戶關閉窗口時立即銷毀窗口及其所有正在運行的回調。 這可能對您不利,具體取決於您當前的 Tkinter 活動,尤其是在使用tkinter.after
(定期回調)時。 您可能正在使用一個回調來處理一些數據並寫入磁盤......在這種情況下,您顯然希望數據寫入完成而不會被突然殺死。
最好的解決方案是使用標志。 因此,當用戶請求關閉窗口時,您將其標記為標志,然后對其做出反應。
(注意:我通常將 GUI 設計為封裝良好的類和單獨的工作線程,並且我絕對不使用“全局”(我使用類實例變量代替),但這是一個簡單的、精簡的示例來演示當用戶關閉窗口時,Tk 如何突然終止您的定期回調......)
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:
# NOTE: In most code, you'll wanna force a close here via
# "exit" if the window failed to destroy. Just ensure that
# you have no code after your `mainloop()` call (at the
# bottom of this file), since the exit call will cause the
# process to terminate immediately without running any more
# code. Of course, you should NEVER have code after your
# `mainloop()` call in well-designed code anyway...
# exit(0)
pass
root.after_idle(periodic_call)
root.mainloop()
此代碼將向您展示WM_DELETE_WINDOW
處理程序即使在我們的自定義 period_call periodic_call()
正忙於工作/循環中間時也會運行!
我們使用了一些相當誇張的.after()
值:500 毫秒。 這只是為了讓您很容易看到定期通話忙時關閉與否之間的區別......如果您在號碼更新時關閉,您將看到WM_DELETE_WINDOW
發生在您的定期通話期間“忙於處理:真”。 如果您在數字暫停時關閉(意味着此時未處理定期回調),您會看到關閉發生在“不忙”時。
在實際使用中,您的.after()
將使用 30-100 毫秒之類的時間來獲得響應式 GUI。 這只是一個演示,幫助您了解如何保護自己免受 Tk 默認的“關閉時立即中斷所有工作”行為的影響。
總之:讓WM_DELETE_WINDOW
處理程序設置一個標志,然后定期檢查該標志,並在安全時手動.destroy()
窗口(當您的應用程序完成所有工作時)。
PS:您也可以使用WM_DELETE_WINDOW
詢問用戶是否真的要關閉窗口; 如果他們回答“否”,您就不會設置標志。 這很簡單。 您只需在WM_DELETE_WINDOW
中顯示一個消息框並根據用戶的回答設置標志。
您應該使用 destroy() 關閉 tkinter 窗口。
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.destroy).pack()
root.mainloop()
解釋:
root.quit()
上面的行只是繞過了root.mainloop()
即root.mainloop()
如果執行了quit()
命令,仍然會在后台運行。
root.destroy()
當destroy()
命令消失時root.mainloop()
即root.mainloop()
停止。
因此,當您只想退出程序時,您應該使用root.destroy()
,因為它會停止 mainloop()`。
但是如果你想運行一些無限循環並且你不想破壞你的 Tk 窗口並且想在root.mainloop()
行之后執行一些代碼,那么你應該使用root.quit()
。 前任:
from Tkinter import *
def quit():
global root
root.quit()
root = Tk()
while True:
Button(root, text="Quit", command=quit).pack()
root.mainloop()
#do something
最簡單的代碼是:
from tkinter import *
window = Tk()
隱藏窗口: window.withdraw()
出現窗口: window.deiconify()
從窗口退出: exit()
從窗口退出(如果您已制作 .exe 文件):
from tkinter import *
import sys
window = Tk()
sys.exit()
當然,您必須放置一個按鈕並在函數中使用上面的代碼,以便您可以在按鈕的命令部分鍵入函數的名稱
嘗試簡單版本:
import tkinter
window = Tk()
closebutton = Button(window, text='X', command=window.destroy)
closebutton.pack()
window.mainloop()
或者,如果您想添加更多命令:
import tkinter
window = Tk()
def close():
window.destroy()
#More Functions
closebutton = Button(window, text='X', command=close)
closebutton.pack()
window.mainloop()
您可以使用:
root = Tk()
def func():
print('not clossed')
root.protocol('wm_delete_window', func)
root.mainloop()
如何處理Python Tkinter程序中的窗口關閉事件(用戶單擊“ X”按鈕)?
我說更簡單的方法是使用break
命令,比如
import tkinter as tk
win=tk.Tk
def exit():
break
btn= tk.Button(win, text="press to exit", command=exit)
win.mainloop()
或使用sys.exit()
import tkinter as tk
import sys
win=tk.Tk
def exit():
sys.exit
btn= tk.Button(win, text="press to exit", command=exit)
win.mainloop()
使用closeEvent
def closeEvent(self, event):
# code to be executed
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.