Reputation: 65
I have coded a small game using python turtle, and it seems like whenever I close the turtle window manually it gives me an error, but if the game finished running and then I close it's fine. I think it has something to do with the ontimer part of the code, but I'm not sure how to fix it.
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()
The error I get when I exit the code manually is this:
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"
Upvotes: 3
Views: 3032
Reputation: 46669
It is caused by the for loop
inside enemy_move()
. If you try to exit the application by destroying the window, the for loop may still be running and so accessing the already destroyed canvas raises the exception.
You can check game_on
at the beginning of each iteration of the for loop:
def enemy_move():
...
for p in enemies:
# exit the function is game_on is False
if not game_on:
return
...
...
Then you need to set game_on
to False
before destroying the window. It can be done using tkinter.protocol()
(as turtle
is built on 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()
Upvotes: 4