回答:
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は、閉じるボタンの1つの古典的な変更を示しました。
もう1つは、閉じるボタンでウィンドウを最小化することです。
あなたは持っていることによって、この動作を再現することができますアイコン化方法を
されたプロトコルメソッドの第二引数。
これは、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]とボタンの2つの新しい終了オプションをユーザーに提供します。
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年の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
、カスタムperiodic_call()
が作業/ループの最中にビジー状態でもハンドラーが実行されることを示しています!
.after()
500ミリ秒というかなり誇張された値を使用します。これは、ちょうどあなたが定期的なコールがビジー状態の間、閉じ、あるいはないとの違いを確認するのは非常に簡単にそれを作るためのものです...あなたは近い数字が更新している間場合は、ことがわかりますWM_DELETE_WINDOW
が起こっている間、あなたの定期的な呼び出しが「でしたビジー処理:True」。数字が一時停止しているときにクローズすると(定期的なコールバックがその時点で処理されないことを意味します)、「ビジーでない」間にクローズが発生したことがわかります。
実際の使用では、.after()
30〜100ミリ秒のようなものを使用して、応答性の高いGUIを作成します。これは、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()