Tkinter에서 창 닫기 이벤트를 어떻게 처리합니까?


답변:


178

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()

2
(:의 트위스트 반응 오브젝트 등) 반드시 외부 메인 루프는 그 목적을 위해 제공하는 어떤 smenatics으로 중지 할 당신은 독립적으로 또는 Tkinter의 이벤트 루프를 유지 트위스트 같은 것을 사용하는 경우 (예 : reactor.stop () 트위스트 용)
브라이언 잭

4
Windows의 Python 2.7에서 Tkinter하위 모듈 메시지 상자가 없었습니다. 내가 사용import tkMessageBox as messagebox
IronManMark20

이 답변과 코드를 다른 사람 / 다른 곳에서 복사했다는 것을 알려야한다고 생각합니다.
Christian Dean

1
잘 모르겠습니다. 원래 게시 한 코드가 아닙니다.
매트 그레고리

나를 위해 작동하지 않습니다. 창을 강제 종료 할 때 (예 : Alt + F4) 그래픽 중단에 대한 고전적인 파이썬의 혼란스러운 반응을 변경하지 않습니다.
Apostolos

29

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버튼 도 제공 합니다.


12

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올바른 장소 에서만 확인 하면됩니다.


4

이 사실을 주목 한 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하고 사용자의 답변에 따라 플래그를 설정합니다.


1

간단한 버전을 사용해보십시오 :

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()

질문은 일반적인 버튼 컨트롤이 아니라 창을 닫는 OS의 X 버튼에 관한 것입니다.
user1318499

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.