簡體   English   中英

如何處理 Tkinter 中的窗口關閉事件?

[英]How do I handle the window close event in Tkinter?

如何在 Python Tkinter 程序中處理窗口關閉事件(用戶單擊“X”按鈕)?

Tkinter 支持一種稱為協議處理程序的機制。 這里,術語協議是指應用程序和窗口管理器之間的交互。 最常用的協議稱為WM_DELETE_WINDOW ,用於定義當用戶使用窗口管理器顯式關閉窗口時會發生什么。

您可以使用protocol方法為此協議安裝處理程序(小部件必須是TkToplevel小部件):

這里有一個具體的例子:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM