Error When Opening Different Windows In Turtle Using Tkinter
Solution 1:
A number of changes need to be made to get a design that works:
First, when you use turtle within tkinter, you need to use embedded turtle (i.e. TurtleScreen
, RawTurtle
) not standalone turtle (Screen
, Turtle
).
Since TurtleScreen
doesn't want to be a Toplevel
instance, I swapped the maze and menu windows.
The turtle screen.clear()
method is highly destructive -- besides clearing the screen, it undoes bindings, background colors, tracer setings, etc. and kills all the turtles. So we have to program accordingly.
Don't call screen.bye()
if you plan to use the window again. Turtle has a distance()
method, you don't have to reinvent it.
Finally, turtles wander a floating point plan. If you save the coordinates of your walls, they won't match turtle positions as turtles accumulate error. You need to coerce the comparison to integer.
Below is my attempt to rework your code to address the above issues:
from turtle import TurtleScreen, RawTurtle
from time import monotonic as my_timer
import tkinter as tk
# tk constants
TK_BG_COLOR = "light green"
FONT = ("comic sans ms", 20)
BUTTON_HEIGHT = 2
BUTTON_WIDTH = 10
PAD_X = 40
PAD_Y = 25
# turtle contants
BG_COLOR = 'black'
WALL_SHAPE = 'square'
WALL_COLOR = 'red'
WALL_SIZE = 24
PLAYER_SHAPE = 'classic'
PLAYER_COLOR = 'white'
CURSOR_SIZE = 20
level_1 = [
'*************************',
'*S***** ********',
'* ******* *** *** *',
'** T **** *** ** *',
'**** ****** **** *** ** *',
'**** ** **** *** ** *',
'*** ** * ** ** *',
'**** * * T ******* * *',
'*** *** ** T ** * **',
'*T * ******** ** **',
'** ***** **** ** *',
'** ***T************** *',
'* ** *** **** ** T *',
'*T * *** ***** ****',
'** * ** **** * *',
'** * ** * ** ***** * **',
'** ** ** *** **',
'****** *** T **** **',
'** ** ** * ** ** **** **',
'*E* ** ** * ***** **** **',
'* * ** ** * ***',
'* * *** **** *****',
'* *********** *T** **',
'* * ** *',
'*************************'
]
level_2 = [
'*************************',
'*******S ********',
'**T ******* *** *** *',
'*** **** *** ** *',
'**** ****** **** *** ** *',
'**** ** **** *** ** *',
'*** ** * ** ** *',
'**** * * T ******* * *',
'*** *** ** T ** * **',
'*T * ******** ** **',
'** ***** **** ** *',
'** ***T************** *',
'* ** *** **** ** T *',
'*T * *** ***** ****',
'** * ** **** * *',
'** * ** * ** ***** * **',
'** ** ** *** **',
'****** *** T **** **',
'** ** ** * ** ** **** **',
'** ** ** * *** ** **** **',
'*T* ** ** * ***',
'* * *** **** *****',
'* *********** *T** **',
'* * ** E*',
'*************************'
]
levels = [("", None), ("Level 2", level_1), ("Level 2", level_2)]
class Walls(RawTurtle):
def __init__(self, canvas):
super().__init__(canvas)
self.shape(WALL_SHAPE)
self.color(WALL_COLOR)
self.penup()
class Player(RawTurtle):
def __init__(self, canvas):
super().__init__(canvas)
self.shape(PLAYER_SHAPE)
self.color(PLAYER_COLOR)
self.penup()
self.setheading(270)
self.gold = 0
def move_up(self):
self.setheading(90)
if (int(self.xcor()), int(self.ycor()) + WALL_SIZE) not in wall_coordinates:
self.sety(self.ycor() + WALL_SIZE)
def move_down(self):
self.setheading(270)
if (int(self.xcor()), int(self.ycor()) - WALL_SIZE) not in wall_coordinates:
self.sety(self.ycor() - WALL_SIZE)
def move_left(self):
self.setheading(180)
if (int(self.xcor()) - WALL_SIZE, int(self.ycor())) not in wall_coordinates:
self.setx(self.xcor() - WALL_SIZE)
def move_right(self):
self.setheading(0)
if (int(self.xcor()) + WALL_SIZE, int(self.ycor())) not in wall_coordinates:
self.setx(self.xcor() + WALL_SIZE)
def has_collided(self, other):
return self.distance(other) < 5
def print_score(self):
print("Your total score is: {} ".format(self.gold))
class Treasure(RawTurtle):
def __init__(self, canvas, x, y):
super().__init__(canvas)
self.shape('circle')
self.color('yellow')
self.penup()
self.goto(x, y)
self.gold = 100
def destroy(self):
self.hideturtle()
class Finish(RawTurtle):
def __init__(self, canvas, x, y):
super().__init__(canvas)
self.shape('square')
self.color('green')
self.penup()
self.goto(x, y)
def load_level(level):
global player
hide_menu()
title, maze = levels[level]
root.title(title)
player = Player(screen) # recreate as it's destroyed by screen.clear()
set_up_maze(maze)
# rebind turtle key bindings as they're unbound by screen.clear()
screen.onkey(player.move_up, 'Up')
screen.onkey(player.move_down, 'Down')
screen.onkey(player.move_left, 'Left')
screen.onkey(player.move_right, 'Right')
screen.listen()
level_finished = False
start_time = my_timer()
while not level_finished:
for treasure in treasures:
if player.has_collided(treasure):
player.gold += treasure.gold
treasure.destroy()
treasures.remove(treasure)
for end in end_points:
if player.has_collided(end):
level_finished = True
screen.update()
screen.clear()
screen.bgcolor(BG_COLOR) # redo as it's undone by clear()
screen.tracer(0) # redo as it's undone by clear()
show_menu()
end_time = my_timer()
total_time = end_time - start_time
player.print_score()
print("Total time was {:.2f} seconds".format(total_time))
def set_up_maze(maze):
walls = Walls(screen)
for y, row in enumerate(maze): # get the character co ordinates
for x, character in enumerate(row):
screen_x = -288 + (x * WALL_SIZE) # calculate the screen co ordinates
screen_y = 288 - (y * WALL_SIZE)
if character == '*':
walls.goto(screen_x, screen_y) # make the * characters into walls
walls.stamp()
wall_coordinates.append((screen_x, screen_y))
elif character == 'S': # make the player start point
player.goto(screen_x, screen_y)
elif character == 'T': # make the treasure spawn points
treasures.append(Treasure(screen, screen_x, screen_y))
elif character == 'E': # make the end point
end_points.append(Finish(screen, screen_x, screen_y))
def hide_menu():
menu.withdraw()
def show_menu():
menu.deiconify()
menu.update()
# lists
wall_coordinates = []
treasures = []
end_points = []
root = tk.Tk()
root.title("Maze game")
root.resizable(width=False, height=False)
canvas = tk.Canvas(root, width=700, height=700)
canvas.pack()
screen = TurtleScreen(canvas)
screen.bgcolor(BG_COLOR)
screen.tracer(0)
player = None
menu = tk.Toplevel(root)
menu.title("Maze game")
menu.config(bg=TK_BG_COLOR)
menu.geometry("250x600+600+100")
menu.resizable(width=False, height=False)
title_label = tk.Label(menu, text="Maze Game", font=FONT, bg=TK_BG_COLOR)
title_label.grid(row=0, column=0, padx=PAD_X, pady=PAD_Y)
level_1_btn = tk.Button(menu, text="Level 1", font=FONT, height=BUTTON_HEIGHT, width=BUTTON_WIDTH, bg=TK_BG_COLOR, command=lambda: load_level(1))
level_1_btn.grid(row=1, column=0, padx=PAD_X, pady=PAD_Y)
level_2_btn = tk.Button(menu, text="Level 2", font=FONT, height=BUTTON_HEIGHT, width=BUTTON_WIDTH, bg=TK_BG_COLOR, command=lambda: load_level(2))
level_2_btn.grid(row=2, column=0, padx=PAD_X, pady=PAD_Y)
close_btn = tk.Button(menu, text="Exit", font=FONT, height=BUTTON_HEIGHT, width=BUTTON_WIDTH, bg=TK_BG_COLOR, command=quit)
close_btn.grid(row=3, column=0, padx=PAD_X, pady=PAD_Y)
screen.mainloop()
Post a Comment for "Error When Opening Different Windows In Turtle Using Tkinter"