繁体   English   中英

使用 tkinter 在海龟中打开不同的 windows 时出错

[英]Error when opening different windows in turtle using tkinter

我在 Python 3.8 中使用带有 tkinter 的海龟时遇到问题。 仍然是编程新手,所以提前感谢!

我有一个 tkinter window ,您可以在其中选择玩第一级或第二级,每次启动程序时,任何一个级别都可以工作,但是一旦您完成该级别并尝试另一个级别,包括同一级别,我就会出错。

错误信息:

"Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Kev\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "C:/Users/Kev/IdeaProjects/HelloWorld/Games/Maze.py", line 49, in load_level_2
    set_up_maze(levels[2])  # choose what level to load
  File "C:/Users/Kev/IdeaProjects/HelloWorld/Games/Maze.py", line 254, in set_up_maze
    walls.goto(screen_x, screen_y)  # make the  * characters into walls
  File "C:\Users\Kev\AppData\Local\Programs\Python\Python38\lib\turtle.py", line 1776, in goto
    self._goto(Vec2D(x, y))
  File "C:\Users\Kev\AppData\Local\Programs\Python\Python38\lib\turtle.py", line 3158, in _goto
    screen._pointlist(self.currentLineItem),
  File "C:\Users\Kev\AppData\Local\Programs\Python\Python38\lib\turtle.py", line 755, in _pointlist
    cl = self.cv.coords(item)
  File "<string>", line 1, in coords
  File "C:\Users\Kev\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2761, in coords
    self.tk.call((self._w, 'coords') + args))]
_tkinter.TclError: invalid command name ".!canvas"

代码:

import turtle
import math
from time import monotonic as my_timer
import tkinter as tk

# turtle variables
bg_color = "black"
wall_shape = "square"
wall_color = "red"
player_shape = "classic"
player_color = "white"


def load_level_1():
    hide_root()
    main_level1 = turtle.Screen()
    main_level1.bgcolor(bg_color)
    main_level1.title("Level 1")
    main_level1.setup(700, 700)
    main_level1.tracer(0)
    print("try to set level up")
    set_up_maze(levels[1])
    print("level set up")
    start_time = my_timer()

    level_finished = False
    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):
                print("Finish reached")
                level_finished = True

        main_level1.update()
    main_level1.clear()
    main_level1.bye()
    show_root()

    end_time = my_timer()
    total_time = end_time - start_time
    player.print_score()
    print("Total time was {:.2f} seconds".format(total_time))


def load_level_2():
    hide_root()
    main_level2 = turtle.Screen()
    main_level2.bgcolor(bg_color)
    main_level2.title("Level 2")
    main_level2.setup(700, 700)
    main_level2.tracer(0)
    print("try to set level up")
    set_up_maze(levels[2])  # choose what level to load
    print("level set up")

    start_time = my_timer()
    level_finished = False

    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):
                print("Finish reached")
                level_finished = True

        main_level2.update()

    main_level2.clear()
    main_level2.bye()
    show_root()

    print("finished")
    end_time = my_timer()
    total_time = end_time - start_time
    player.print_score()
    print("Total time was {:.2f} seconds".format(total_time))


# create the pen
class Walls(turtle.Turtle):
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape(wall_shape)
        self.color(wall_color)
        self.penup()
        self.speed(0)


class Player(turtle.Turtle):
    def __init__(self):
        turtle.Turtle.__init__(self)
        self.shape(player_shape)
        self.color(player_color)
        self.penup()
        self.speed(0)
        self.gold = 0
        self.settiltangle(-90)

    def move_up(self):
        self.settiltangle(90)
        if (self.xcor(), self.ycor() + 24) not in wall_coordinates:
            self.goto(self.xcor(), self.ycor() + 24)

    def move_down(self):
        self.settiltangle(-90)
        if (self.xcor(), self.ycor() - 24) not in wall_coordinates:
            self.goto(self.xcor(), self.ycor() - 24)

    def move_left(self):
        self.settiltangle(180)
        if (self.xcor() - 24, self.ycor()) not in wall_coordinates:
            self.goto(self.xcor() - 24, self.ycor())

    def move_right(self):
        self.settiltangle(0)
        if (self.xcor() + 24, self.ycor()) not in wall_coordinates:
            self.goto(self.xcor() + 24, self.ycor())

    def has_collided(self, other):
        a = self.xcor() - other.xcor()
        b = self.ycor() - other.ycor()
        distance = math.sqrt((a ** 2) + (b ** 2))

        if distance < 5:
            return True
        else:
            return False

    def print_score(self):
        print("Your total score is: {} ".format(self.gold))


class Treasure(turtle.Turtle):
    def __init__(self, x, y):
        turtle.Turtle.__init__(self)
        self.shape("circle")
        self.color("yellow")
        self.penup()
        self.speed(0.5)
        self.gold = 100
        self.goto(x, y)

    def destroy(self):
        self.goto(2000, 2000)
        self.hideturtle()


class Finish(turtle.Turtle):
    def __init__(self, x, y):
        turtle.Turtle.__init__(self)
        self.shape("square")
        self.color("green")
        self.penup()
        self.speed(0.5)
        self.goto(x, y)


# lists
levels = [""]
wall_coordinates = []
treasures = []
end_points = []

level_template = ["*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************",
                  "*************************"]

level_1 = ['*************************',
           '*S*****          ********',
           '*  E*******  *** ***    *',
           '** T        **** *** ** *',
           '**** ****** **** *** ** *',
           '**** **   ****   *** ** *',
           '***  ** * **         ** *',
           '**** *  * T  ******* *  *',
           '***  ***   ** T   ** * **',
           '*T *     ******** **   **',
           '** ***** ****        ** *',
           '**   ***T************** *',
           '* ** ***  ****    **  T *',
           '*T * ***       ***** ****',
           '** * **  ****        *  *',
           '** * **  * **  ***** * **',
           '**      ** **  ***     **',
           '****** ***    T   **** **',
           '**  ** ** * ** ** **** **',
           '*E* ** ** * ***** **** **',
           '* * ** **   *         ***',
           '* *       ***  **** *****',
           '* ***********  *T**    **',
           '*              *    **  *',
           '*************************']

level_2 = ['*************************',
           '*******S        E********',
           '**T *******  *** ***    *',
           '***        ****  *** ** *',
           '**** ****** **** *** ** *',
           '**** **   ****   *** ** *',
           '***  ** * **         ** *',
           '**** *  * T  ******* *  *',
           '***  ***   ** T   ** * **',
           '*T *     ******** **   **',
           '** ***** ****        ** *',
           '**   ***T************** *',
           '* ** ***  ****    **  T *',
           '*T * ***       ***** ****',
           '** * **  ****        *  *',
           '** * **  * **  ***** * **',
           '**      ** **  ***     **',
           '****** ***    T   **** **',
           '**  ** ** * ** ** **** **',
           '** ** ** * *** ** **** **',
           '*T* ** **   *         ***',
           '* *       ***  **** *****',
           '* ***********  *T**    **',
           '*              *    ** E*',
           '*************************']

levels.append(level_1)
levels.append(level_2)


def set_up_maze(level):
    for y in range(len(level)):  # get the character co ordinates
        for x in range(len(level[y])):
            character = level[y][x]  # save the character coordinates
            screen_x = -288 + (x * 24)  # calculate the screen co ordinates
            screen_y = 288 - (y * 24)

            if character == "*":
                walls.goto(screen_x, screen_y)  # make the  * characters into walls
                walls.stamp()
                wall_coordinates.append((screen_x, screen_y))

            if character == "S":        # make the player start point
                player.goto(screen_x, screen_y)

            if character == "T":    # make the treasure spawn points
                treasures.append(Treasure(screen_x, screen_y))

            if character == "E":    # make the end point
                end_points.append(Finish(screen_x, screen_y))


walls = Walls()
player = Player()

# key bindings
turtle.listen()
turtle.onkey(player.move_up, "Up")
turtle.onkey(player.move_down, "Down")
turtle.onkey(player.move_left, "Left")
turtle.onkey(player.move_right, "Right")

# tk variables
tk_bg_color = "light green"
font = ("comic sans ms", 20)
btn_height = 2
btn_width = 10
pad_x = 40
pad_y = 25


def hide_root():
    root.withdraw()


def show_root():
    root.deiconify()
    root.update()


root = tk.Tk()
root.title("Maze game")
root.config(bg=tk_bg_color)
root.geometry("250x600+600+100")
root.resizable(width=False, height=False)

title_label = tk.Label(root, 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(root, text="Level 1", font=font, height=btn_height, width=btn_width, bg=tk_bg_color,
                        command=load_level_1)
level_1_btn.grid(row=1, column=0, padx=pad_x, pady=pad_y)
level_2_btn = tk.Button(root, text="Level 2", font=font, height=btn_height, width=btn_width, bg=tk_bg_color,
                        command=load_level_2)
level_2_btn.grid(row=2, column=0, padx=pad_x, pady=pad_y)
close_btn = tk.Button(root, text="Exit", font=font, height=btn_height, width=btn_width, bg=tk_bg_color,
                      command=quit)
close_btn.grid(row=3, column=0, padx=pad_x, pady=pad_y)

root.mainloop()

需要进行许多更改才能获得有效的设计:

首先,当您在 tkinter 中使用海龟时,您需要使用嵌入式海龟(即TurtleScreenRawTurtle )而不是独立海龟( ScreenTurtle )。

由于TurtleScreen不想成为Toplevel实例,我交换了迷宫和菜单 windows。

海龟screen.clear()方法具有很强的破坏性——除了清除屏幕外,它还撤消绑定、背景 colors、跟踪器设置等,并杀死所有海龟。 所以我们必须相应地编程。

如果您打算再次使用 window,请不要调用screen.bye() Turtle 有一个distance()方法,你不必重新发明它。

最后,海龟游走一个浮点计划。 如果您保存墙壁的坐标,它们将与海龟的位置不匹配,因为海龟会累积误差。 您需要强制与 integer 进行比较。

以下是我尝试修改您的代码以解决上述问题:

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()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM