- 问题现象
我有一个主TK界面, 同时又创建了一个新的独立的TK窗口. 这个新的TK窗口设置为topmost, 用于超时提醒的. 这个窗口虽然是topmost的, 但是可能没有输入焦点. 我想设置一个快捷键, 用于关闭此窗口. 也就是说, 在另外的线程中关闭tk窗口. 采用的方法是在另外线程中调用root.destroy()
. 结果就是偶尔好使, 有时会出现错误:Tcl_AsyncDelete: async handler deleted by the wrong thread
. 出现错误后, 整个python解释器会退出. - 解决方案1
在另外的线程中使用代码:
这样可以强制在root线程中运行代码, 但是有时候仍然出同样的错误, 只不过频率会降低一些.root.after(0,lambda:root.destroy())
如果代码修改为如下(等待1秒钟之后再调用):
基本上不再出现问题, 不过副作用是关闭窗口时会延迟1秒.root.after(1000,lambda:root.destroy())
方案1, 我觉得不是特别完美. - 解决方案2
完全避免跨线程调用. 思路是在tk窗口线程中轮询某个信号量, 当出现指定的信号执行相应的动作. 代码如下:
经过实践验证, 完美解决了我的问题.def _loop_query(root:tk.Tk, callback:callable): '''tk线程内轮询''' callback() # 调用回调函数 root.after(300, lambda: _loop_query(root, callback)) def Alarm(msg:str='提醒!',showTime:float=None): '''提醒用户, 计划采用的方式是弹出提醒窗口; msg为可选提醒信息 showTime: 显示时间, 单位秒, 超过显示时间自动销毁 '23090801_AlarmClose': 关闭消息, 当收到此消息时关闭窗口 ''' start_time = time.time() # 窗口创建时间 def _check_close(): '''检查是否收到关闭通知, 收到后关闭窗口''' notice = message.get_notice('23090901_CloseAlarm') if notice!=None and notice.time>start_time: root.destroy() # 销毁窗口 root = tk.Tk() root.title('提醒框') root.geometry("200x100+20+20") tk.Label(root, text=msg).pack(side=tk.TOP,anchor=tk.W) root.wm_attributes("-topmost", 1) if showTime!=None: root.after(int(showTime*1000), lambda: root.destroy()) # 自动销毁窗口 _loop_query(root, _check_close) # 循环检查, 需要时关闭窗口 root.mainloop()
测试代码如下:import TkExt import time import message import Common from TkExt import * @Common.run_in_thread def test_func(): Alarm('hzq test.') print('hzq test2.') test_func() time.sleep(30) message.pub_notice('23090901_CloseAlarm') time.sleep(1000)
python tk编程出现: Tcl_AsyncDelete: async handler deleted by the wrong thread
发布时间 2023-09-09 22:32:27作者: 顺其自然,道法自然