Reputation: 311
Python 2.7
I have written a run
method for my Tkinter GUI rather than using the standard mainloop
, and it always exits on an error when I close the window, even after implementing a WM_DELETE_WINDOW
protocol as advised elsewhere on SO. I tried invoking exit
in the protocol callback and return
ing from the loop, but Python always goes through the loop one last time. Why is this?
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
Here is the run loop:
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()
Result:
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
Upvotes: 0
Views: 886
Reputation: 1554
Without the mainloop, tkinter cannot get the WM_DELETE_WINDOW
message to call your exit function. (Or rather, it can only catch anything during the ~millisecond of the update_idletasks
call, as it won't queue since tkinter doesn't have an event loop (and thus queue) going since you never start one.) It can't catch signals if it can't communicate with the Window Manager (system), and it can't communicate if it isn't looping.
To solve it, just use the event/main loop. Make your run
function save any state it needs and call itself after
whatever interval you wish.
On another note, don't use time.sleep
with tkinter- it prevents it from doing anything (and also the 40ms remaining of sleep is probably longer than the rest of the loop, so you'd have 41 ms of waiting and 0.5 ms of clickability). Instead, just carefully configure your root.after
statements (you can calculate things in them, too)
Upvotes: 1