Tkinter 循环永远不会干净地退出
Tkinter Loop Never Exits Cleanly
Python 2.7
我已经为我的 Tkinter GUI 编写了一个 run
方法,而不是使用标准 mainloop
,并且当我关闭 window 时它总是因错误而退出,即使在实施之后SO 上其他地方建议的 WM_DELETE_WINDOW
协议。我尝试在协议回调中调用 exit
并从循环中调用 return
,但 Python 总是最后一次通过循环。这是为什么?
class FrameApp(object):
def __init__(self):
...
self.rootWin.protocol("WM_DELETE_WINDOW", self.callback_destroy)
self.winRunning = False
def callback_destroy(self):
self.winRunning = False
self.rootWin.destroy() # go away, window
exit() # GET OUT
这是 运行 循环:
def run(self):
last = -infty
self.winRunning = True
...
while self.winRunning:
# 4.a. Calc geometry
self.calcFunc( self.get_sliders_as_list() )
# 4.b. Send new coords to segments
self.simFrame.transform_contents()
# 4.d. Wait remainder of 40ms
elapsed = time.time() * 1000 - last
if elapsed < 40:
time.sleep( (40 - elapsed) / 1000.0 )
# 4.e. Mark beginning of next loop
last = time.time() * 1000
# 4.f. Update window
if not self.winRunning: # This does not solve the problem
return # still tries to call 'update',
# and never exits cleanly
self.canvas.update()
# don't know how to prevent these from being called
# again after the window is destroyed
self.rootWin.update_idletasks()
结果:
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 972, in
update_idletasks
self.tk.call('update', 'idletasks')
_tkinter.TclError: can't invoke "update" command: application has been destroyed
没有主循环,tkinter 无法获得 WM_DELETE_WINDOW
消息来调用您的退出函数。 (或者更确切地说,它只能在 update_idletasks
调用的~毫秒期间捕获任何内容,因为它不会排队,因为 tkinter 没有事件循环(因此没有队列),因为你从未开始过。)如果无法与 Window 管理器(系统)通信,则无法捕获信号,如果不循环,则无法通信。
要解决它,只需使用event/main循环。让你的 run
函数保存它需要的任何状态,并在你希望的任何时间间隔调用自己 after
。
另一方面,不要将 time.sleep
与 tkinter 一起使用 - 它会阻止它做任何事情(而且剩余的 40 毫秒睡眠时间可能比循环的其余部分长,所以你会41 毫秒的等待时间和 0.5 毫秒的可点击性)。相反,只需仔细配置您的 root.after
语句(您也可以计算其中的内容)
Python 2.7
我已经为我的 Tkinter GUI 编写了一个 run
方法,而不是使用标准 mainloop
,并且当我关闭 window 时它总是因错误而退出,即使在实施之后SO 上其他地方建议的 WM_DELETE_WINDOW
协议。我尝试在协议回调中调用 exit
并从循环中调用 return
,但 Python 总是最后一次通过循环。这是为什么?
class FrameApp(object):
def __init__(self):
...
self.rootWin.protocol("WM_DELETE_WINDOW", self.callback_destroy)
self.winRunning = False
def callback_destroy(self):
self.winRunning = False
self.rootWin.destroy() # go away, window
exit() # GET OUT
这是 运行 循环:
def run(self):
last = -infty
self.winRunning = True
...
while self.winRunning:
# 4.a. Calc geometry
self.calcFunc( self.get_sliders_as_list() )
# 4.b. Send new coords to segments
self.simFrame.transform_contents()
# 4.d. Wait remainder of 40ms
elapsed = time.time() * 1000 - last
if elapsed < 40:
time.sleep( (40 - elapsed) / 1000.0 )
# 4.e. Mark beginning of next loop
last = time.time() * 1000
# 4.f. Update window
if not self.winRunning: # This does not solve the problem
return # still tries to call 'update',
# and never exits cleanly
self.canvas.update()
# don't know how to prevent these from being called
# again after the window is destroyed
self.rootWin.update_idletasks()
结果:
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 972, in update_idletasks self.tk.call('update', 'idletasks') _tkinter.TclError: can't invoke "update" command: application has been destroyed
没有主循环,tkinter 无法获得 WM_DELETE_WINDOW
消息来调用您的退出函数。 (或者更确切地说,它只能在 update_idletasks
调用的~毫秒期间捕获任何内容,因为它不会排队,因为 tkinter 没有事件循环(因此没有队列),因为你从未开始过。)如果无法与 Window 管理器(系统)通信,则无法捕获信号,如果不循环,则无法通信。
要解决它,只需使用event/main循环。让你的 run
函数保存它需要的任何状态,并在你希望的任何时间间隔调用自己 after
。
另一方面,不要将 time.sleep
与 tkinter 一起使用 - 它会阻止它做任何事情(而且剩余的 40 毫秒睡眠时间可能比循环的其余部分长,所以你会41 毫秒的等待时间和 0.5 毫秒的可点击性)。相反,只需仔细配置您的 root.after
语句(您也可以计算其中的内容)