使用 tkinter 在海龟中打开不同的窗口时出错 [英] Error when opening different windows in turtle using tkinter

查看:19
本文介绍了使用 tkinter 在海龟中打开不同的窗口时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a problem using turtle with tkinter in Python 3.8. Still new to programming so thanks in advance!

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

I have a tkinter window where you can chose to play either level one or two, every time you start the program either level will work but once you have finished the level and try another level, including the same level I get an error.

错误信息:

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

推荐答案

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

A number of changes need to be made to get a design that works:

首先,当你在tkinter中使用turtle时,你需要使用embeddedturtle(即TurtleScreenRawTurtle)而不是standalone 海龟 (Screen, Turtle).

First, when you use turtle within tkinter, you need to use embedded turtle (i.e. TurtleScreen, RawTurtle) not standalone turtle (Screen, Turtle).

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

Since TurtleScreen doesn't want to be a Toplevel instance, I swapped the maze and menu windows.

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

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.

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

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

这篇关于使用 tkinter 在海龟中打开不同的窗口时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆