【问题标题】:Error when opening different windows in turtle using tkinter使用 tkinter 在海龟中打开不同窗口时出错
【发布时间】:2020-06-04 09:26:10
【问题描述】:

我在 Python 3.8 中使用带有 tkinter 的 turtle 时遇到问题。还是编程新手,提前致谢!

我有一个 tkinter 窗口,您可以在其中选择播放第一级或第二级,每次启动程序时,任何一个级别都可以工作,但是一旦您完成该级别并尝试另一个级别,包括同一级别,我会收到错误.

错误信息:

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

【问题讨论】:

    标签: python python-3.x tkinter turtle-graphics python-turtle


    【解决方案1】:

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

    首先,当你在 tkinter 中使用 turtle 时,你需要使用 embedded turtle(即TurtleScreenRawTurtle)而不是standalone turtle(ScreenTurtle)。

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

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-13
      • 2016-03-06
      • 2021-02-15
      • 1970-01-01
      相关资源
      最近更新 更多