首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Tkinter画布没有显示窗口

Tkinter画布没有显示窗口
EN

Stack Overflow用户
提问于 2022-08-15 19:27:47
回答 1查看 95关注 0票数 -1

我一直在制作蛇游戏,但是当我运行它的时候,它的表现就像它应该做的那样,但是没有显示的tkinter窗口。当我删除'while‘循环时,它会显示窗口,但是没有任何东西在里面,因为可视化在循环中。我使用pycharm pro 2021.3.2和python 3.9。这是我的代码:

代码语言:javascript
运行
复制
from tkinter import *
import random as rm
import time
import keyboard as kb

# Creating the window and the canvas
root = Tk()
root.geometry("390x390")
root.resizable(False, False)

c = Canvas(root, width=390, height=390, bg="white")
c.pack()

#Creating lists of all possible x and y coordinates
board = []
for i in range(39):
    for j in range(39):
        board.append([i, j])





# Creating the Snake class
class Snake:
    def __init__(self):
        # Initializating:
        self.head_cords = [20, 20]    # A list that contains the x and the y coordinate of the Snake head
        self.body_cords = [[17, 20], [18, 20], [19, 20]]    # A list with smaller lists with coordinates of each Snake block
        self.direction = "right"    # The directions the Snake is facing
        self.lenght = 4    # The Snake length (Not the game score, that is the number of eaten Apples, but the number of the Snake blocks + 1 for the head)

    def move(self):
        # Creating the movement function

        # Movineg the body
        self.body_cords.pop(0)
        self.body_cords.append(self.head_cords)

        # Moving the head according to the Snake direction
        if self.direction == "up":
            self.head_cords[1] = self.head_cords[1] - 1
        if self.direction == "down":
            self.head_cords[1] = self.head_cords[1] + 1
        if self.direction == "left":
            self.head_cords[0] = self.head_cords[0] - 1
        if self.direction == "right":
            self.head_cords[0] = self.head_cords[0] + 1

    def check_dead(self):
        # Creating the function to check if the Snake is dead basing on 2 conditions:

        # 1: If the Snake hits the wall
        if self.head_cords[0] > 39 or self.head_cords[0] < 1 or self.head_cords[1] > 39 or self.head_cords[1] < 1:
            return True
        # 2: If the Snake hits itself
        elif self.head_cords in self.body_cords:
            return True
        else:
            return False

    def grow(self):
        # Creating the growth function (it will be called if the Snake eats an Apple)
        self.body_cords.insert(0, [self.body_cords[0][0], self.body_cords[0][1]])

Jordan = Snake()    # Creating an instance of the Snake class


# Creating the Apple class
class Apple:
    def __init__(self):
        # Initializating the x and the y coordinates


        # To place the Apple somewhere on the board, we should check which cells are already occupied by the Snake
        snake_positions = []
        for pos in Jordan.body_cords:
            snake_positions.append(pos)
        snake_positions.append(Jordan.head_cords)


        # Then we choose which cells are not occupied by the Snake
        possinle_positions = []
        for pos in board:
            if pos in snake_positions:
                pass
            else:
                possinle_positions.append(pos)

        # And choose one of them randomly
        rm.choice(possinle_positions)
        self.pos = rm.choice(possinle_positions)

    def check_eaten(self):
        # Creating the function to check if the Apple it eaten by the Snake
        if Jordan.head_cords == self.pos:
            return True
        else:
            return False

    def __del__(self):
        # Creating the Apple delete function
        print("+1 Apple")



nessy = Apple()    # Creating an instance of the Apple class


# Creating the visualization function
def visualizate():

    # Clearing everything
    c.create_rectangle(0, 0, 390, 390, fill="white")

    # Visualizing the Snake

    # Head
    c.create_rectangle(Jordan.head_cords[0]*10-9, Jordan.head_cords[0]*10, Jordan.head_cords[1]*10-9, Jordan.head_cords[1]*10,
                       fill="yellow", outline="black", width=2)
    # Body
    for block in Jordan.body_cords:
        c.create_rectangle(block[0]*10-9, block[0]*10, block[1]*10-9, block[1]*10, fill="yellow", outline="white", width=2)

    # Visualizing the Apple
    c.create_rectangle(nessy.pos[0]*10-9, nessy.pos[0]*10, nessy.pos[1]*10-9, nessy.pos[1]*10, fill="red", outline="black", width=1)




# Creating the contlors

# If you are from stackoverflow - ignore the controls. They do nothing now
def change_direction(id):
    if id == 1:
        Jordan.direction = "up"
    if id == 2:
        Jordan.direction = "down"
    if id == 3:
        Jordan.direction = "left"
    if id == 4:
        Jordan.direction = "right"

kb.add_hotkey("up", change_direction(1))
kb.add_hotkey("down", change_direction(2))
kb.add_hotkey("left", change_direction(3))
kb.add_hotkey("right", change_direction(4))



# The game cycle
while True:
    visualizate()    # Visualizing what we have

    # Checking if the Snake is dead
    if Jordan.check_dead():
        time.sleep(5)
        root.destroy()    #Closing 
        print("You died")
        break


    # Checking if the Apple is eaten
    if nessy.check_eaten():
        del nessy
        Jordan.grow()
        nessy = Apple()

    Jordan.move()

    print("move")
    time.sleep(1)










root.mainloop()
EN

回答 1

Stack Overflow用户

发布于 2022-08-16 10:56:34

root.mainloop()是一个特殊的循环,它从系统中获取键/鼠标事件,将它们发送到小部件、更新小部件和(重新)在屏幕上绘制所有内容。但是您的while True永远运行,所以它不能运行mainloop()。您可能必须在分隔的thread中运行它循环。或者在循环内部,您必须使用root.update()来允许tkinter执行一个循环(和更新窗口)。或者您应该使用root.after(milliseconds, function)而不是while、-loop和sleep()来周期性地执行一个循环。

tkinter可以bind()键到函数,它不需要模块keyboard (BTW:在我的Linux模块上,keyboard需要作为root (keyboard)运行)

如果要使用change_direction("up")而不是change_direction(1),那么可以将函数简化为def change_direction(direction): jordan.direction = direction。但就我而言,add_hotkey()可能需要没有()和没有参数的函数名称--也就是所谓的"callback" --或者您必须使用lambda来创建回调kb.add_hotkey("up", lambda:change_direction("up"))

您在create_rectangle中使用了错误的值,它会创建大小不同的大矩形。您可以使用create_rectangle创建白色矩形以删除先前的元素,但它不会从画布中删除先前的矩形,而是只将它们隐藏在白色矩形后面--在某个时刻,您可能有数百个隐藏的矩形--您应该使用canvas.delete('all')。或者您应该保留对象的ID -- apple_id = c.create_rectange(...) --然后将对象移动到新位置c.move(apple_id, ...)

最后一个问题:当你移动时,你把头加到身体上,但它是带有to元素的列表,当它附加到其他列表时,它不重复列表,但是它只发送引用,然后当你改变头部的位置,它在正文中也改变位置-它需要使用.copy()来追加重复的元素self.body_cords.append(self.head_cords.copy())

编辑:i还将函数change_direction()移到Snake.change_direction()

编辑: ==提供TrueFalse,因此在check_eaten()中可以使用单行return (jordan.head_cords == self.pos)而不使用if/else编写更短的代码

这段代码适用于我

代码语言:javascript
运行
复制
import tkinter as tk  # PE8: `import *` is not preferred
import random
import time

# --- classes ---  # PEP8: all classes directly after imports

# Creating the Snake class
class Snake:
    def __init__(self):
        # Initializating:
        self.head_cords = [20, 20]    # A list that contains the x and the y coordinate of the Snake head
        self.body_cords = [[17, 20], [18, 20], [19, 20]]    # A list with smaller lists with coordinates of each Snake block
        self.direction = "right"    # The directions the Snake is facing
        self.lenght = 4    # The Snake length (Not the game score, that is the number of eaten Apples, but the number of the Snake blocks + 1 for the head)

    def move(self):
        # Creating the movement function

        # Movineg the body
        self.body_cords.pop(0)
        self.body_cords.append(self.head_cords.copy())

        # Moving the head according to the Snake direction
        if self.direction == "up":
            self.head_cords[1] -= 1    # shorter with `-=` / `+=`
        if self.direction == "down":
            self.head_cords[1] += 1    # shorter with `-=` / `+=`
        if self.direction == "left":
            self.head_cords[0] -= 1    # shorter with `-=` / `+=`
        if self.direction == "right":
            self.head_cords[0] += 1    # shorter with `-=` / `+=`

    def check_dead(self):
        # Creating the function to check if the Snake is dead basing on 2 conditions:

        # 1: If the Snake hits the wall
        if self.head_cords[0] > 39 or self.head_cords[0] < 1 or self.head_cords[1] > 39 or self.head_cords[1] < 1:
            print('collide wall')
            return True
        # 2: If the Snake hits itself
        elif self.head_cords in self.body_cords:
            print('collide itself')
            return True
        else:
            return False

    def grow(self):
        # Creating the growth function (it will be called if the Snake eats an Apple)
        self.body_cords.insert(0, [self.body_cords[0][0], self.body_cords[0][1]])

    def change_direction(self, direction):
        self.direction = direction
        
# Creating the Apple class
class Apple:
    def __init__(self):
        # Initializating the x and the y coordinates

        # To place the Apple somewhere on the board, we should check which cells are already occupied by the Snake
        snake_positions = []
        for pos in jordan.body_cords:
            snake_positions.append(pos)
        snake_positions.append(jordan.head_cords)

        # Then we choose which cells are not occupied by the Snake
        possinle_positions = []
        for pos in board:
            if pos not in snake_positions:
                possinle_positions.append(pos)

        # And choose one of them randomly
        self.pos = random.choice(possinle_positions)

    def check_eaten(self):
        # Creating the function to check if the Apple it eaten by the Snake
        #if jordan.head_cords == self.pos:
        #    return True
        #else:
        #    return False
        return (jordan.head_cords == self.pos)  # `==` gives `True` or `False`

    def __del__(self):
        # Creating the Apple delete function
        print("+1 Apple")

# --- functions ---  # PEP8: all functions directly after classes

# Creating the visualization function
def visualizate():

    # Clearing everything
    #c.create_rectangle(0, 0, 390, 390, fill="white")
    c.delete('all')
    
    # Visualizing the Snake

    # Head
    x = jordan.head_cords[0]*10
    y = jordan.head_cords[1]*10
    c.create_rectangle(x-9, y-9, x, y, fill="green", outline="black", width=2)
    
    # Body
    for block in jordan.body_cords:
        x = block[0]*10
        y = block[1]*10
        c.create_rectangle(x-9, y-9, x, y, fill="yellow", outline="white", width=2)

    # Visualizing the Apple
    x = nessy.pos[0]*10
    y = nessy.pos[1]*10
    c.create_rectangle(x-9, y-9, x, y, fill="red", outline="black", width=1)

# --- main ---

# Creating the window and the canvas
root = tk.Tk()
root.geometry("390x390")
root.resizable(False, False)

c = tk.Canvas(root, width=390, height=390, bg="white")
c.pack()

# Creating lists of all possible x and y coordinates
board = []
for i in range(39):
    for j in range(39):
        board.append([i, j])

jordan = Snake()  # Creating an instance of the Snake class  # PEP8: `CamelCaseNames` only for classes - it helps to recognize class in code
nessy  = Apple()  # Creating an instance of the Apple class

root.bind("<Up>",    lambda event:jordan.change_direction("up"))
root.bind("<Down>",  lambda event:jordan.change_direction("down"))
root.bind("<Left>",  lambda event:jordan.change_direction("left"))
root.bind("<Right>", lambda event:jordan.change_direction("right"))

# The game cycle
while True:
    visualizate()    # Visualizing what we have

    print('check snake')
    # Checking if the Snake is dead
    if jordan.check_dead():
        print("You died")
        root.update()
        time.sleep(5)
        root.destroy()    # Closing 
        break

    print('check apple')
    # Checking if the Apple is eaten
    if nessy.check_eaten():
        del nessy
        jordan.grow()
        nessy = Apple()

    print("move")
    jordan.move()

    root.update()   # allow `mainloop()` to update window
    time.sleep(0.5)

#root.mainloop()  # no need if you use `root.update()`
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73365612

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档