Matplotlib封装Particle类后补丁动画失效问题排查
问题:将plt.Circle封装到Particle类后,Matplotlib动画无法正常运行的原因
原始可运行代码
import numpy as np from matplotlib import pyplot as plt from matplotlib import animation fig = plt.figure() fig.set_dpi(100) fig.set_size_inches(7, 6.5) ax = plt.axes(xlim=(0, 10), ylim=(0, 10)) patch = plt.Circle((5, -5), 0.75, fc='y') def init(): patch.center = (5, 5) ax.add_patch(patch) return patch, def animate(i): x, y = patch.center x = 5 + 3 * np.sin(np.radians(i)) y = 5 + 3 * np.cos(np.radians(i)) patch.center = (x, y) return patch, anim = animation.FuncAnimation(fig, animate, init_func=init, frames=36, interval=20, blit=True) HTML(anim.to_jshtml())
修改后的问题代码
import numpy as np from matplotlib import pyplot as plt from matplotlib import animation fig = plt.figure() fig.set_dpi(100) fig.set_size_inches(7, 6.5) ax = plt.axes(xlim=(0, 10), ylim=(0, 10)) class Particle: def __init__(self,x,y): self.x = x self.y = y def show(self): return plt.Circle((self.x, self.y), 0.75, fc='y') p = Particle(5, -5) def init(): ax.add_patch(p.show()) return p.show(), def animate(i): p.x = 5 + 3 * np.sin(np.radians(i)) p.y = 5 + 3 * np.cos(np.radians(i)) return p.show(), anim = animation.FuncAnimation(fig, animate, init_func=init, frames=36, interval=20, blit=True) HTML(anim.to_jshtml())
问题原因及解决方法
核心问题
每次调用p.show()都会创建一个全新的plt.Circle对象,而不是修改已经添加到坐标轴上的那个圆:
- 在
init()里,你调用p.show()添加了一个圆到坐标轴,但返回的是另一个新创建的圆对象,和坐标轴上的不是同一个。 - 在
animate()里,每次又创建新的圆返回,Matplotlib的blitting机制需要跟踪同一个图形元素的变化,而不是不断创建新元素,这就导致动画无法正确更新已有的圆,反而可能不断叠加新圆(但blitting模式下不会正确渲染)。
修复方案
修改Particle类,让它内部保存一个plt.Circle实例,而不是每次创建新的:
import numpy as np from matplotlib import pyplot as plt from matplotlib import animation fig = plt.figure() fig.set_dpi(100) fig.set_size_inches(7, 6.5) ax = plt.axes(xlim=(0, 10), ylim=(0, 10)) class Particle: def __init__(self,x,y): self.circle = plt.Circle((x, y), 0.75, fc='y') # 初始化时创建一次圆实例 def get_circle(self): return self.circle def update_pos(self, x, y): self.circle.center = (x, y) # 直接修改已有圆的中心 p = Particle(5, 5) def init(): ax.add_patch(p.get_circle()) return p.get_circle(), def animate(i): x = 5 + 3 * np.sin(np.radians(i)) y = 5 + 3 * np.cos(np.radians(i)) p.update_pos(x, y) return p.get_circle(), anim = animation.FuncAnimation(fig, animate, init_func=init, frames=36, interval=20, blit=True) HTML(anim.to_jshtml())
关键改动说明
- Particle类初始化时就创建一个plt.Circle实例并保存为属性,后续不再创建新的。
- 通过
update_pos方法直接修改已有圆的中心坐标,而不是重新创建圆。 get_circle方法始终返回同一个圆实例,保证Matplotlib的动画机制能跟踪到同一个图形元素的变化。
内容的提问来源于stack exchange,提问作者Lenard Kasselmann
相关产品推荐
相关产品推荐

