Bir Python Tkinter programında pencere kapatma olayını (kullanıcı 'X' düğmesine tıklar) nasıl işleyebilirim?
Bir Python Tkinter programında pencere kapatma olayını (kullanıcı 'X' düğmesine tıklar) nasıl işleyebilirim?
Yanıtlar:
Tkinter, protokol işleyicileri adı verilen bir mekanizmayı destekler . Burada protokol terimi, uygulama ile pencere yöneticisi arasındaki etkileşimi ifade eder. En sık kullanılan protokoleWM_DELETE_WINDOW
ve kullanıcı pencere yöneticisini kullanarak bir pencereyi açıkça kapattığında ne olacağını tanımlamak için kullanılır.
Bu protokol için bir işleyici yüklemek için protocol
yöntemi kullanabilirsiniz (pencere öğesi bir veya pencere öğesi olmalıdır ):Tk
Toplevel
Burada somut bir örnek var:
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
bir alt modül mesaj kutusu yoktu. Kullandımimport tkMessageBox as messagebox
Matt, kapat düğmesinin klasik bir değişikliğini gösterdi.
Diğeri ise kapat düğmesinin pencereyi simge durumuna küçültmesidir.
Bu davranışı, iconify yönteminin protokol yönteminin ikinci bağımsız değişkeni
olmasını sağlayarak yeniden oluşturabilirsiniz .
İşte Windows 7 ve 10'da test edilmiş çalışan bir örnek:
# 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()
Bu örnekte, kullanıcıya iki yeni çıkış seçeneği sunuyoruz:
klasik Dosya → Çıkış ve ayrıca Escdüğme.
Tkinter etkinliğine bağlı olarak ve özellikle Tkinter.after kullanılırken, bu etkinliği durdurmak destroy()
- protokol (), bir düğme, vb. Kullanarak bile - bu etkinliği sonlandırmak yerine ("hata yürütürken") bozacaktır. . Hemen hemen her durumda en iyi çözüm bir bayrak kullanmaktır. İşte nasıl kullanılacağına dair basit, saçma bir örnek (çoğunuzun buna ihtiyaç duymadığına eminim! :)
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()
Bu, grafik aktivitesini güzelce sonlandırır. Sadece running
doğru yer (ler) de kontrol etmeniz gerekiyor .
Bunu dikkatime sunduğunuz için Apostolos'un cevabına teşekkür etmek istiyorum. İşte 2019 yılında Python 3 için daha net bir açıklama ve örnek kodla çok daha ayrıntılı bir örnek.
destroy()
pencereyi kapattığında pencereyi ve çalışan tüm geri aramalarını anında yok edeceğine (veya özel bir pencere kapatma işleyicisine sahip olmadığına) dikkat edin .Mevcut Tkinter etkinliğinize bağlı olarak ve özellikle kullanırken bu sizin için kötü olabilir. tkinter.after
(periyodik geri aramalar) . Bazı verileri işleyen ve diske yazan bir geri arama kullanıyor olabilirsiniz ... bu durumda, açıkça veri yazmanın aniden öldürülmeden bitmesini istersiniz.
Bunun için en iyi çözüm bayrak kullanmaktır. Dolayısıyla, kullanıcı pencerenin kapatılmasını istediğinde, bunu bir bayrak olarak işaretler ve ardından buna tepki verirsiniz.
(Not: Normalde GUI'leri güzel bir şekilde kapsüllenmiş sınıflar ve ayrı çalışan iş parçacıkları olarak tasarlıyorum ve kesinlikle "global" kullanmıyorum (bunun yerine sınıf örnek değişkenleri kullanıyorum), ancak bunun gösterilmesi için basit, soyulmuş bir örnek olması gerekiyor. Tk, kullanıcı pencereyi kapattığında periyodik geri aramalarınızı nasıl aniden sonlandırır ...)
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()
Bu kod, WM_DELETE_WINDOW
işleyicinin bizim özel sistemimizperiodic_call()
işimiz / döngülerin ortasında meşgulken !
Oldukça abartılı .after()
değerler kullanıyoruz: 500 milisaniye. Bu sadece periyodik çağrı meşgulken kapatmak veya olmamak arasındaki farkı görmenizi çok kolaylaştırmak içindir ... Numaralar güncellenirken kapatırsanız , periyodik aramanız sırasındaWM_DELETE_WINDOW
gerçekleştiğini göreceksiniz " meşgul işleme: Doğru ". Numaralar duraklatılmış haldeyken kapatırsanız (yani periyodik geri aramanın o anda işlenmediği anlamına gelir), kapanmanın "meşgul değilken" gerçekleştiğini görürsünüz.
Gerçek dünya kullanımında, .after()
duyarlı bir GUI'ye sahip olmak için 30-100 milisaniye gibi bir şey kullanırsınız. Bu, kendinizi Tk'nin varsayılan "kapanırken tüm işleri anında durdur" davranışına karşı nasıl koruyacağınızı anlamanıza yardımcı olacak bir gösterimdir.
Özetle: WM_DELETE_WINDOW
İşleyicinin bir bayrak ayarlamasını .destroy()
sağlayın ve ardından bu bayrağı periyodik olarak ve güvenli olduğunda pencereyi manuel olarak kontrol edin (uygulamanız tüm işlerle bittiğinde).
NOT: Eger kullanabilirsiniz WM_DELETE_WINDOW
için sormak onlar gerçekten pencereyi kapatmak istiyorsanız kullanıcıyı; ve hayır cevabını verirlerse bayrağı koymazsınız. Çok basit. Siz sadece bir mesaj kutusu gösterin WM_DELETE_WINDOW
ve kullanıcının cevabına göre bayrağı ayarlayın.
Basit Sürümü Deneyin:
import tkinter
window = Tk()
closebutton = Button(window, text='X', command=window.destroy)
closebutton.pack()
window.mainloop()
Veya Daha Fazla Komut Eklemek İstiyorsanız:
import tkinter
window = Tk()
def close():
window.destroy()
#More Functions
closebutton = Button(window, text='X', command=close)
closebutton.pack()
window.mainloop()
from tkinter import*
root=Tk()
exit_button=Button(root,text="X",command=root.quit)
root.mainloop()