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
相关产品推荐
相关产品推荐

