Python Tkinter Canvas:基于类实现多球同时下落的问题
解决Tkinter Canvas多小球同时下落问题
你的代码里,每个小球的fall方法用了for循环加sleep,而且是逐个调用这个方法——Tkinter是单线程的,这种写法会把主事件循环卡死,导致小球只能一个接一个下落。要实现同时下落,得用Tkinter自带的after方法来调度动画,别用阻塞式的sleep和循环。
下面是修改后的完整代码:
import tkinter as tk from random import randint class Circle: def __init__(self, canvas): self.canvas = canvas self.canvas_h = canvas.winfo_height() self.w = randint(50, 300) self.x = randint(0, canvas.winfo_width() - self.w) self.y = 0 self.ball = canvas.create_oval(self.x, -self.w, self.x + self.w, 0, fill=['black','red','blue','lime','yellow','grey'][randint(0,5)]) self.step = randint(5, 20) def fall(self): # 获取当前小球的位置 x1, y1, x2, y2 = self.canvas.coords(self.ball) # 判断是否已经落到画布外 if y2 > self.canvas_h: self.canvas.delete(self.ball) return # 移动一步 self.canvas.move(self.ball, 0, self.step) # 20毫秒后继续调用fall,实现动画循环 self.canvas.after(20, self.fall) c = tk.Canvas(width=1000, height=1000) c.pack() # 强制更新画布,确保能获取到正确的宽高 c.update() count = 10 balls = [Circle(c) for _ in range(count)] # 同时启动所有小球的下落动画 for ball in balls: ball.fall() c.mainloop()
关键修改说明:
- 移除了
time.sleep和for循环,改用after(20, self.fall)递归调度动画。after方法会在指定时间后把任务加入主事件队列,不会阻塞循环,这样所有小球的移动事件会被交替处理,视觉上就是同时下落。 - 初始化画布后调用
c.update(),确保能获取到画布实际的宽高(之前的winfo_reqheight是请求高度,不一定是实际渲染后的高度)。 - 在
fall方法里通过coords获取小球当前位置,判断底部是否超出画布高度,超出则删除小球并停止递归,避免无效的动画调用。
内容的提问来源于stack exchange,提问作者willko22
相关产品推荐
相关产品推荐

