问题
When I click on the X (close) button while running a infinite loop of drawing in Turtle graphics, some error messages occurs.
Here is an example:
import turtle
wn = turtle.Screen()
tess = turtle.Turtle()
while True:
tess.forward(50)
tess.left(120)
tess.forward(50)
wn.mainloop()
When I close the window, then the following error message shows up.
Traceback (most recent call last):
File "/Users/user/Downloads/test.py", line 8, in <module>
tess.forward(50)
File "/Users/user/anaconda3/lib/python3.6/turtle.py", line 1637, in forward
self._go(distance)
File "/Users/user/anaconda3/lib/python3.6/turtle.py", line 1605, in _go
self._goto(ende)
File "/Users/user/anaconda3/lib/python3.6/turtle.py", line 3178, in _goto
self._pencolor, self._pensize, top)
File "/Users/user/anaconda3/lib/python3.6/turtle.py", line 545, in _drawline
self.cv.coords(lineitem, *cl)
File "<string>", line 1, in coords
File "/Users/user/anaconda3/lib/python3.6/tkinter/__init__.py", line 2463, in coords
self.tk.call((self._w, 'coords') + args))]
_tkinter.TclError: invalid command name ".!canvas"
I am wondering how such error messages could be avoided.
Is there any way like using "protocol" method with "WM_DELETE_WINDOW" option of the Tk class from the tkinter module?
回答1:
Yes, this can indeed be avoided by registering a function (I called it on_close
, but you can choose any function name) to intercept the window closing event.
One tricky thing is that protocol
is a method of the Tk
class. In non-turtle tkinter
usage, you create your Tk
object yourself as your top-level (or "root") widget. As we are using the widgets provided by the turtle
module, how can we access the top-level widget? It's available through the winfo_toplevel
method of the canvas (which can be accessed through the turtle
module or the screen object).
The error you've observed is caused by the infinite loop trying to draw stuff when the window (and with it, the canvas) is already gone. So the next tricky thing is, how can we prevent it from attempting that? As recommended by Apostolos' answer to "How do I handle the window close event in Tkinter?", we can use a global boolean flag. (Just like Apostolos, I called it running
. But you can choose any name that makes sense to you.) With that, our loop isn't so infinite anymore, it's a conditional loop. Because the window might be closed between the three turtle movements, I check the flag there, too:
import turtle
wn = turtle.Screen()
canvas = wn.getcanvas() # or, equivalently: turtle.getcanvas()
root = canvas.winfo_toplevel()
tess = turtle.Turtle()
def on_close():
global running
running = False
root.protocol("WM_DELETE_WINDOW", on_close)
running = True
while running:
tess.forward(50)
if not running:
break
tess.left(120)
if not running:
break
tess.forward(50)
On my computer, it also works without error message without the two
if not running:
break
parts, but that might just be lucky timing, so I wouldn't rely on that. (Unless someone can explain why that should always suffice.)
Note: I didn't need to call root.destroy()
in on_close
, because the loop is the last thing to run in the program, anyway. (note that I also don't call mainloop()
) So when we break out of the loop, or the loop finishes because its condition is no longer true, the program finishes and closes the window.
来源:https://stackoverflow.com/questions/50654793/how-to-detect-x-close-button-in-python-turtle-graphics