How to detect X (close) button in python Turtle graphics?

故事扮演 提交于 2019-12-08 06:08:41

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!