답변:
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()
Tkinter
하위 모듈 메시지 상자가 없었습니다. 내가 사용import tkMessageBox as messagebox
Matt는 닫기 버튼의 고전적인 변형을 보여주었습니다.
다른 하나는 닫기 버튼으로 창을 최소화하는 것입니다.
당신은 필요에 의해이 동작을 재현 할 수 있습니다 아이콘 화 방법은
수 프로토콜 메서드의 두 번째 인수.
다음은 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()
를 사용하는 경우 protocol (), 단추 등을 사용 하여이 활동을 중지하면이 활동을 종료하는 것이 아니라 "실행하는 동안"오류가 발생합니다. . 거의 모든 경우에 가장 좋은 해결책은 플래그를 사용하는 것입니다. 다음은 그것을 사용하는 방법에 대한 간단하고 어리석은 예입니다 (그러나 대부분의 사람들이 필요하지 않다고 확신합니다! :)
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
올바른 장소 에서만 확인 하면됩니다.
이 사실을 주목 한 Apostolos의 답변에 감사드립니다. 2019 년 파이썬 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
사용자 정의 periodic_call()
가 바쁘 더라도 핸들러가 실행 됨을 보여줍니다 !
우리는 몇 가지 과장된 .after()
값을 사용 합니다 : 500 밀리 초. 이것은 단지 정기적 인 전화가 통화 중, 또는없는 동안 폐쇄의 차이를 볼 수 있도록 숫자가 업데이트하는 동안 가까이, 당신은이 것을 볼 수 있다면 ... 아주 쉽게 그것을 확인하기위한 것입니다 WM_DELETE_WINDOW
무슨 일이 있었 동안 당신의주기적인 호출은 "이었다 처리 중 : True " 번호가 일시 중지 된 상태에서 종료하면 (현재 콜백이 처리되고 있지 않다는 의미), 통화 중이 아닌 동안 종료가 발생한 것을 볼 수 있습니다.
실제 사용에서는 .after()
반응 형 GUI를 만들기 위해 30-100 밀리 초와 같은 것을 사용합니다. 이것은 Tk의 기본 "닫을 때 모든 작업을 즉시 중단"동작으로부터 자신을 보호하는 방법을 이해하는 데 도움이되는 데모 일뿐입니다.
요약 : WM_DELETE_WINDOW
핸들러가 플래그를 설정 한 다음 .destroy()
안전 할 때 (앱이 모든 작업을 수행 할 때) 창을 주기적으로 수동으로 확인하십시오 .
PS : 당신은 또한 사용할 수 있습니다 WM_DELETE_WINDOW
하도록 요청 그들이 정말로 창을 닫으려면 사용자; 그들이 "아니오"라고 대답하면, 깃발을 설정하지 않습니다. 매우 간단합니다. 당신은 당신의 메시지 상자를 표시 WM_DELETE_WINDOW
하고 사용자의 답변에 따라 플래그를 설정합니다.
간단한 버전을 사용해보십시오 :
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()