【问题标题】:Python turtle game error _tkinter.TclError: invalid command name ".!canvas"Python海龟游戏错误_tkinter.TclError:无效的命令名称“.!canvas”
【发布时间】:2021-08-10 00:37:06
【问题描述】:

我使用 python turtle 编写了一个小游戏,似乎每当我手动关闭海龟窗口时都会出现错误,但如果游戏完成运行然后我关闭它就可以了。我认为这与代码的 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"

【问题讨论】:

    标签: python tkinter python-turtle


    【解决方案1】:

    这是由enemy_move() 内部的for loop 引起的。如果您尝试通过销毁窗口来退出应用程序,则 for 循环可能仍在运行,因此访问已销毁的画布会引发异常。

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

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

    那么你需要在销毁窗口之前将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()
    

    【讨论】:

    • 您能否再解释一下我如何实现您答案的第二部分?我不确定它会如何工作
    • wn._root.protocol("WM_DELETE_WINDOW", on_quit) 绑定一个回调,该回调将由窗口管理器在销毁窗口之前触发。在回调中,你可以决定是否销毁窗口,并在销毁窗口之前做任何你想做的事情。详情可参考tk doc
    • 我明白了!非常感谢您的回答
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-14
    • 2020-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多