Tkinter项目循环内sleep()函数异常,无法正常展示3D立方体缩放动画
解决Tkinter中sleep导致动画不逐帧显示的问题
问题原因
- Tkinter是单线程GUI框架,调用
time.sleep()会直接阻塞主事件循环,所有界面绘制操作都会被延迟到所有sleep执行完毕后才一次性渲染,因此你看不到中间帧的变化,只能看到最后一帧。 sys.stdout.flush()是针对控制台输出的刷新操作,和Tkinter的GUI界面刷新完全无关,所以无效。
解决方案
使用Tkinter自带的after(delay_ms, callback)方法实现帧延迟,它会在指定毫秒后异步调用回调函数,不会阻塞主循环,能让界面及时更新每一帧。
修改后的完整代码
from tkinter import * # 像素绘制相关函数 tk = Tk() tk.attributes('-fullscreen', True) canvas = Canvas(tk, width=1366, height=768, background='#fff') canvas.pack() colors = [[(255, 255, 255) for i in range(223)] for i in range(321)] def color(a, b, c): def hex_str(val): val = val % 256 return f"{val:02x}" return f'#{hex_str(a)}{hex_str(b)}{hex_str(c)}' def colr(lis): return color(lis[0], lis[1], lis[2]) def fill_rect(a, b, c, d, e): a = a + 683 b = -b + 384 fill_color = e if isinstance(e, str) else colr(e) canvas.create_rectangle(a+2, b+2, c+a+2, d+b+2, fill=fill_color, outline='') for x in range(c): for y in range(d): if 0 <= (a+x) <= 320 and 0 <= (b+y) <= 222: colors[a+x][b+y] = e def set_pixel(a, b, e): fill_rect(a, b, 1, 1, e) def get_pixel(x, y): if 0 <= x <= 320 and 0 <= y <= 222: return colors[x][y] else: return (0, 0, 0) # 3D绘制相关函数 def draw_line(xa, ya, xb, yb): canvas.create_line(round(xa)+683, -round(ya)+384, round(xb)+683, -round(yb)+384, fill="black", width=1) focal_length = 100 def dproj(xa, ya, za, xb, yb, zb): draw_line(focal_length*xa/za, focal_length*ya/za, focal_length*xb/zb, focal_length*yb/zb) # 立方体顶点定义 a = [-1, -1, 1] b = [1, -1, 1] c = [1, 1, 1] d = [-1, 1, 1] e = [-1, -1, 3] f = [1, -1, 3] g = [1, 1, 3] h = [-1, 1, 3] # 动画帧循环函数 def animate_frame(i): if i >= 300: return # 动画结束条件 canvas.delete("all") global focal_length focal_length = i # 绘制立方体所有边 dproj(a[0], a[1], a[2], b[0], b[1], b[2]) dproj(a[0], a[1], a[2], d[0], d[1], d[2]) dproj(a[0], a[1], a[2], e[0], e[1], e[2]) dproj(c[0], c[1], c[2], b[0], b[1], b[2]) dproj(c[0], c[1], c[2], d[0], d[1], d[2]) dproj(c[0], c[1], c[2], g[0], g[1], g[2]) dproj(f[0], f[1], f[2], b[0], b[1], b[2]) dproj(f[0], f[1], f[2], e[0], e[1], e[2]) dproj(f[0], f[1], f[2], g[0], g[1], g[2]) dproj(h[0], h[1], h[2], d[0], d[1], d[2]) dproj(h[0], h[1], h[2], e[0], e[1], e[2]) dproj(h[0], h[1], h[2], g[0], g[1], g[2]) # 10毫秒后触发下一帧,对应原sleep(0.01) tk.after(10, animate_frame, i+1) # 启动动画并进入主事件循环 animate_frame(0) tk.mainloop()
关键修改点说明
- 移除了
time.sleep(),改用tk.after()实现非阻塞的帧延迟,确保每帧绘制后界面能及时刷新。 - 将原for循环改为递归的
animate_frame函数,通过参数i控制帧进度,直到达到300帧时停止。 - 添加了
tk.mainloop()启动Tkinter主事件循环,原代码缺少这一行也是界面无法正常更新的原因之一。 - 优化了颜色转换函数的代码逻辑,简化十六进制字符串生成(不影响核心功能)。
内容的提问来源于stack exchange,提问作者IskallMAN
相关产品推荐
相关产品推荐

