You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter Canvas窗口不显示:贪吃蛇游戏运行异常求助

问题解决:Tkinter窗口无法显示的贪吃蛇游戏修复

核心问题原因

你用while True循环阻塞了Tkinter的mainloop()——Tkinter的UI渲染完全依赖mainloop()处理事件和更新界面,你的死循环在mainloop()之前运行,导致UI线程根本没机会初始化和显示窗口。另外time.sleep()也会阻塞UI线程,同样破坏Tkinter的事件循环机制。

修复方案及代码调整

我们用Tkinter自带的after()方法替代死循环和time.sleep(),它能在不阻塞UI线程的前提下定时执行游戏逻辑。同时还会修复几个小bug:

调整后的完整代码

from tkinter import *
import random as rm
import keyboard as kb

# 创建窗口和画布
root = Tk()
root.geometry("390x390")
root.resizable(False, False)

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

# 生成所有可能的坐标点
board = []
for i in range(39):
    for j in range(39):
        board.append([i, j])

# 蛇类定义
class Snake:
    def __init__(self):
        self.head_cords = [20, 20]    # 蛇头坐标
        self.body_cords = [[17, 20], [18, 20], [19, 20]]    # 蛇身各段坐标
        self.direction = "right"    # 当前移动方向
        self.length = 4    # 蛇的总长度(头+身体段数)

    def move(self):
        # 移动蛇身:移除最尾段,将当前蛇头位置加入蛇身
        self.body_cords.pop(0)
        # 注意:这里要复制列表,不然会导致蛇身和蛇头引用同一个列表
        self.body_cords.append(self.head_cords.copy())

        # 根据方向移动蛇头
        if self.direction == "up":
            self.head_cords[1] -= 1
        elif self.direction == "down":
            self.head_cords[1] += 1
        elif self.direction == "left":
            self.head_cords[0] -= 1
        elif self.direction == "right":
            self.head_cords[0] += 1

    def check_dead(self):
        # 判断蛇是否死亡:撞墙或撞自己
        if (self.head_cords[0] >= 39 or self.head_cords[0] < 0 
            or self.head_cords[1] >= 39 or self.head_cords[1] < 0):
            return True
        elif self.head_cords in self.body_cords:
            return True
        else:
            return False

    def grow(self):
        # 蛇吃苹果后增长:在蛇身最前端添加一段
        self.body_cords.insert(0, self.body_cords[0].copy())

Jordan = Snake()    # 创建蛇实例

# 苹果类定义
class Apple:
    def __init__(self):
        # 生成苹果位置:避开蛇的当前位置
        snake_positions = [pos.copy() for pos in Jordan.body_cords]
        snake_positions.append(Jordan.head_cords.copy())

        possible_positions = []
        for pos in board:
            if pos not in snake_positions:
                possible_positions.append(pos)

        self.pos = rm.choice(possible_positions)

    def check_eaten(self):
        # 判断苹果是否被吃掉
        return Jordan.head_cords == self.pos

    def __del__(self):
        print("+1 Apple")

nessy = Apple()    # 创建苹果实例

# 可视化函数
def visualizate():
    # 清空画布
    c.create_rectangle(0, 0, 390, 390, fill="white")

    # 绘制蛇头
    c.create_rectangle(Jordan.head_cords[0]*10, Jordan.head_cords[1]*10,
                       Jordan.head_cords[0]*10+9, Jordan.head_cords[1]*10+9,
                       fill="yellow", outline="black", width=2)
    # 绘制蛇身
    for block in Jordan.body_cords:
        c.create_rectangle(block[0]*10, block[1]*10,
                           block[0]*10+9, block[1]*10+9,
                           fill="yellow", outline="white", width=2)

    # 绘制苹果
    c.create_rectangle(nessy.pos[0]*10, nessy.pos[1]*10,
                       nessy.pos[0]*10+9, nessy.pos[1]*10+9,
                       fill="red", outline="black", width=1)

# 方向控制函数
def change_direction(new_dir):
    # 防止反向移动(比如向右时不能直接向左)
    opposite = {"up":"down", "down":"up", "left":"right", "right":"left"}
    if new_dir != opposite[Jordan.direction]:
        Jordan.direction = new_dir

# 绑定键盘快捷键
kb.add_hotkey("up", lambda: change_direction("up"))
kb.add_hotkey("down", lambda: change_direction("down"))
kb.add_hotkey("left", lambda: change_direction("left"))
kb.add_hotkey("right", lambda: change_direction("right"))

# 游戏主循环(用after实现非阻塞)
def game_loop():
    # 检查蛇是否死亡
    if Jordan.check_dead():
        print("You died")
        root.after(5000, root.destroy)
        return

    # 检查苹果是否被吃掉
    if nessy.check_eaten():
        del nessy
        Jordan.grow()
        nessy = Apple()

    Jordan.move()
    visualizate()

    # 1000毫秒后再次调用自身,实现循环
    root.after(1000, game_loop)

# 启动游戏
game_loop()
root.mainloop()

关键修复点说明

  • 用root.after(1000, game_loop)替代while True和time.sleep(1),让游戏逻辑在Tkinter事件循环的间隙执行,不会阻塞UI渲染。
  • 修复了蛇身移动时的引用问题:之前直接把self.head_cords加入body_cords,导致所有蛇身段都引用同一个列表,现在用copy()创建新列表。
  • 修正了画布绘制坐标的错误:之前的坐标顺序写错了(x和y搞混),现在调整为正确的x1, y1, x2, y2顺序。
  • 优化了方向控制:添加了防止反向移动的逻辑,避免蛇直接撞自己。
  • 修正了边界判断的数值:之前的边界条件>39或<1错误,应该对应>=39或<0(因为坐标是0到38)。

内容的提问来源于stack exchange,提问作者matveygal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 15:15:32