tk.Toplevel选日期后关窗触发_tkinter.TclError错误求助
问题:Tkinter日期选择窗口关闭时触发_tkinter.TclError错误
我编写了一个tk.Toplevel类用于获取用户选择的日期。用户点击日期后,窗口关闭并将日期返回至主程序。虽然能成功获取日期,但窗口关闭时会触发错误:_tkinter.TclError: bad window path name ".!kalender.!dateentry.!toplevel"。
代码如下:
class Kalender(tk.Toplevel): def __init__(self, parent, date=''): Toplevel.__init__(self, parent) # Fenster mittig zentrieren x = (self.winfo_screenwidth() // 2) - (100 // 2) y = (self.winfo_screenheight() // 2) - (50 // 2) self.grab_set() self.geometry('{}x{}+{}+{}'.format(180, 90, x, y)) self.attributes('-toolwindow', True) self.title('Datum auswählen') self.resizable(width=False, height=False) self.date = None self.sel = StringVar() self.cal = DateEntry(self, font="Arial 14", selectmode='day', locale='de_DE', date_pattern="dd.mm.y ", textvariable=self.sel) self.cal.bind("<<DateEntrySelected>>", self.close_window) self.cal.set_date(date) self.cal.grid(row=0, column=0, padx=10, pady=10, sticky=W+E) self.focus_set() def close_window(self, e): self.date = self.cal.get() self.destroy() def show(self): self.deiconify() self.wm_protocol("WM_DELETE_WINDOW", self.close_window) self.wait_window() return self.date cal = Kalender(main_window, d).show()
报错信息:
Exception in Tkinter callback Traceback (most recent call last): File "B:\Python 310\lib\tkinter\__init__.py", line 1921, in __call__ return self.func(*args) File "B:\Python 310\lib\site-packages\tkcalendar\dateentry.py", line 301, in _select self._top_cal.withdraw() File "B:\Python 310\lib\tkinter\__init__.py", line 2269, in wm_withdraw return self.tk.call('wm', 'withdraw', self._w) _tkinter.TclError: bad window path name ".!kalender.!dateentry.!toplevel"
我猜测问题出在Tkinter尝试调用已被销毁的kalender.dateentry组件。请问我哪里出错了?
解决方案
错误原因
当你选择日期触发<<DateEntrySelected>>事件时,close_window方法直接销毁了整个Toplevel窗口。但tkcalendar的DateEntry组件在用户选择日期后,内部还会执行self._top_cal.withdraw()来隐藏它弹出的日历窗口——此时主窗口已经被销毁,DateEntry组件也跟着被回收,导致找不到对应的窗口路径,从而抛出错误。
另外,窗口关闭按钮绑定的close_window方法需要事件参数,但点击叉号关闭时不会传递参数,还会触发另一个参数不匹配的错误(只是当前被TclError掩盖了)。
修改后的代码
import tkinter as tk from tkcalendar import DateEntry class Kalender(tk.Toplevel): def __init__(self, parent, date=''): super().__init__(parent) # 使用super()调用父类构造,更规范 # 窗口居中修正(原计算数值与窗口尺寸不匹配) win_width, win_height = 180, 90 x = (self.winfo_screenwidth() // 2) - (win_width // 2) y = (self.winfo_screenheight() // 2) - (win_height // 2) self.grab_set() self.geometry(f'{win_width}x{win_height}+{x}+{y}') self.attributes('-toolwindow', True) self.title('选择日期') self.resizable(width=False, height=False) self.date = None self.sel = tk.StringVar() self.cal = DateEntry(self, font="Arial 14", selectmode='day', locale='de_DE', date_pattern="dd.mm.y ", textvariable=self.sel) self.cal.bind("<<DateEntrySelected>>", self.on_date_selected) self.cal.set_date(date) self.cal.grid(row=0, column=0, padx=10, pady=10, sticky=tk.W+tk.E) self.focus_set() def on_date_selected(self, e): self.date = self.cal.get() # 延迟10ms销毁窗口,给DateEntry足够时间完成内部操作 self.after(10, self.destroy_window) def destroy_window(self): # 无参的销毁方法,适配窗口关闭按钮的绑定 self.destroy() def show(self): self.deiconify() self.wm_protocol("WM_DELETE_WINDOW", self.destroy_window) self.wait_window() return self.date # 调用示例 # main_window = tk.Tk() # d = "01.01.2024" # cal = Kalender(main_window, d).show() # main_window.mainloop()
关键修改点
- 拆分日期选择与窗口销毁逻辑:将原
close_window拆分为on_date_selected(处理日期获取)和destroy_window(负责窗口销毁),避免在事件回调中直接销毁窗口。 - 延迟销毁窗口:使用
self.after(10, self.destroy_window)让DateEntry有足够时间完成隐藏内部日历窗口的操作,再销毁主窗口。 - 修复窗口关闭按钮绑定:绑定无参的
destroy_window方法,避免点击叉号时的参数不匹配错误。 - 修正窗口居中计算:原代码中居中计算的数值与窗口实际尺寸不符,调整后实现真正的居中显示。
内容的提问来源于stack exchange,提问作者Benaja
相关产品推荐
相关产品推荐

