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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 07:38:32