简体   繁体   English

Tkinter canvas 不显示 window

[英]Tkinter canvas doesn't show the window

I've been making the snake game, but when I run it, it performs like it should, but withown the tkinter window shown.我一直在制作蛇游戏,但是当我运行它时,它的表现就像它应该的那样,但是没有显示的 tkinter window。 When I delete the 'while' cycle, it shows the window, but without anything inside because the visualization is inside the cycle.当我删除“while”循环时,它显示 window,但内部没有任何内容,因为可视化在循环内。 I use pycharm pro 2021.3.2, and python 3.9.我使用 pycharm pro 2021.3.2 和 python 3.9。 Here is my code:这是我的代码:

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

root.mainloop() is special loop which gets key/mouse events from system, sends them to widgets, update widgets and (re)draw all on screen. root.mainloop()是一个特殊的循环,它从系统获取键/鼠标事件,将它们发送到小部件,更新小部件并(重新)在屏幕上绘制所有内容。 But your while True runs forever so it can't run mainloop() .但是你的while True永远运行,所以它不能运行mainloop() You may have to run it loop in separated thread .您可能必须在单独的thread中循环运行它。 Or inside loop you have to use root.update() to allow tkinter to execute one loop (and update window).或者在循环内部,您必须使用root.update()来允许tkinter执行一个循环(并更新窗口)。 Or you should use root.after(milliseconds, function) instead of while -loop and sleep() - to execute one loop periodically.或者您应该使用root.after(milliseconds, function)而不是while -loop 和sleep() - 定期执行一个循环。

tkinter can bind() keys to functions and it doesn't need module keyboard (BTW: on my Linux module keyboard needs to run as root (admin)) tkinter可以bind()键到功能,它不需要模块keyboard (顺便说一句:在我的 Linux 模块keyboard需要以 root 身份运行(管理员))

If you would use change_direction("up") instead change_direction(1) then you could reduce function to def change_direction(direction): jordan.direction = direction .如果您使用change_direction("up")代替change_direction(1)那么您可以将 function 减少到def change_direction(direction): jordan.direction = direction But as for me add_hotkey() may need function's name without () and without arguments - so called "callback" - or you would have to use lambda to create callback kb.add_hotkey("up", lambda:change_direction("up")) But as for me add_hotkey() may need function's name without () and without arguments - so called "callback" - or you would have to use lambda to create callback kb.add_hotkey("up", lambda:change_direction("up"))

You use wrong values in create_rectangle and it creates big rectangles with different sizes.您在create_rectangle中使用了错误的值,它会创建不同大小的大矩形。 You use create_rectangle to create white rectangle to remove previous elements but it doesn't remove previous rectangles from canvas but it only hides them behind white rectanlge - and in some moment you may have hundreds hidden rectangles - you should use canvas.delete('all') .您使用create_rectangle创建白色矩形来删除以前的元素,但它不会从 canvas 中删除以前的矩形,但它只会将它们隐藏在白色矩形后面 - 在某些时候你可能有数百个隐藏的矩形 - 你应该使用canvas.delete('all') OR you should keep object's ID - apple_id = c.create_rectange(...) - and later move objects to new place c.move(apple_id, ...)或者您应该保留对象的 ID - apple_id = c.create_rectange(...) - 然后将对象移动到新位置c.move(apple_id, ...)

Last problem: when you move then you append head to body but it is list with to elements and Python doesn't duplicate list when it append to other list but it send only reference and later when you change head's position then it change also position in body - it needs to use .copy() to append duplicated element self.body_cords.append(self.head_cords.copy()) Last problem: when you move then you append head to body but it is list with to elements and Python doesn't duplicate list when it append to other list but it send only reference and later when you change head's position then it change also position in body - 它需要使用.copy()到 append 重复元素self.body_cords.append(self.head_cords.copy())

EDIT: I also moved function change_direction() into Snake.change_direction()编辑:我还将 function change_direction()移动到Snake.change_direction()

EDIT: == gives True or False so in check_eaten() you can write shorter using single line return (jordan.head_cords == self.pos) without if/else编辑: ==给出TrueFalse因此在check_eaten()中,您可以使用单行return (jordan.head_cords == self.pos)编写更短的代码,而无需if/else


This code works for me这段代码对我有用

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

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

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