Python turtle game error _tkinter.TclError: invalid command name ".!canvas"

Python turtle game error _tkinter.TclError: invalid command name ".!canvas"

我用 python 乌龟编写了一个小游戏,似乎每当我手动关闭乌龟 window 时它都会给我一个错误,但如果游戏结束 运行然后我关闭它就可以了。我认为它与代码的 ontimer 部分有关,但我不确定如何修复它。

import turtle
from random import randint


wn = turtle.Screen()
circle1 = turtle.Turtle(shape = 'circle')
bullet = turtle.Turtle(shape = "circle")
bullet.ht()
bullet.speed(0)
circle1.speed(-1)
circle1.penup()
circle1.ht()
circle1.sety(-270)
circle1.st()

wn.setup(300, 600)

enemies = []
score = 0
prevscore = 1
speed = 10


for i in range(10):
    p = turtle.Turtle(shape='square',visible=False)
    p.speed(0)
    p.penup()
    p.color('blue')
    x = randint(-240,240)
    y = randint(180,360)
    p.goto(x,y)
    p.showturtle()
    enemies.append(p)

def enemy_move():
    global game_on
    global speed
    global score
    global prevscore

    for p in enemies:

        y = p.ycor()
        p.sety(y-speed)

        if p.ycor() < -300  or p.distance(bullet.pos())<30:
            if p.distance(bullet.pos())<30:
                score += 1
            p.hideturtle()
            y = randint(180,360)
            p.sety(y)
            p.showturtle()

        if circle1.isvisible() and p.distance(circle1.pos())<20:
            p.hideturtle()
            circle1.hideturtle()
            game_on = False
            wn.clear()
            circle1.goto(0, 0)
            circle1.write(f"Your final score is {score}", align ="center", font = ("Arial", 26, "normal"))


    if game_on == True:

        if score%10 == 0:

            if score != prevscore:
                speed += 0.5
            prevscore = score

        wn.ontimer(enemy_move,50)

    else:
        return

game_on = True
enemy_move()

def goright():
    if(circle1.xcor() < 130):
        circle1.seth(0)
        circle1.fd(10)
def goleft():
    if(circle1.xcor() > -130):
        circle1.seth(180)
        circle1.fd(10)

def shoot():
    bullet.penup()
    bullet.goto(circle1.pos())
    bullet.seth(90)
    bullet_move()
    bullet.showturtle()

def bullet_move():
    if bullet.ycor() <= 300:
        bullet.sety(bullet.ycor() + 10)
        wn.ontimer(bullet_move, 50)
    else:
        bullet.hideturtle()


wn.listen()

wn.onkeypress(goright, "Right")
wn.onkeypress(goleft, "Left")
wn.onkeypress(shoot, "Up")

wn.mainloop()

我手动退出代码时得到的错误是这样的:

Traceback (most recent call last):
  File "/Users/luke/PycharmProjects/Class Teaching/Game interface example.py", line 1, in <module>
    import game
  File "/Users/luke/PycharmProjects/Class Teaching/game.py", line 31, in <module>
    p.goto(x,y)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/turtle.py", line 1777, in goto
    self._goto(Vec2D(x, y))
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/turtle.py", line 3159, in _goto
    screen._pointlist(self.currentLineItem),
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/turtle.py", line 756, in _pointlist
    cl = self.cv.coords(item)
  File "<string>", line 1, in coords
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 2762, in coords
    self.tk.call((self._w, 'coords') + args))]
_tkinter.TclError: invalid command name ".!canvas"

enemy_move()里面的for loop造成的。如果您尝试通过销毁 window 退出应用程序,for 循环可能仍然是 运行,因此访问已经销毁的 canvas 会引发异常。

您可以在 for 循环的每次迭代开始时检查 game_on

def enemy_move():
    ...
    for p in enemies:
        # exit the function is game_on is False
        if not game_on:
            return
        ...
    ...

那么你需要在销毁window之前将game_on设置为False。可以使用 tkinter.protocol() 完成(因为 turtle 建立在 tkinter 之上):

...
def on_quit():
    global game_on
    game_on = False
    # give some time to stop the for loop before destroying window
    wn._root.after(100, wn._root.destroy)

wn._root.protocol("WM_DELETE_WINDOW", on_quit)

wn.mainloop()