Matplotlib在第一帧后停止动画
我正在尝试给两个子图添加动画,每个子图里面有多条线。我使用的是Matplotlib库,并且用到了FuncAnimation
,这个功能在很多动画示例中都有使用。
使用动画时:
如果我尝试添加动画,结果只显示了第一帧的内容:

不使用动画时:
如果我手动调用我的update_lines
函数,它就能正常工作。

代码:
下面是完整的代码(在main()
中取消注释3行代码可以正常运行,但我希望看到实时更新,所以才尝试使用动画)。
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def make_subplots():
def setup_axes(axes):
for ax in axes:
ax.set_xbound(0, 100) # bound will change as needed.
ax.set_ylim(0, 1) # limit won't change automatically.
def make_lines(axes):
labels = ('a', 'b', 'c')
lines = []
for ax in axes:
ax_lines = []
for label in labels:
x, y = [0], [0]
line, = ax.plot(x, y, label=label) # comma for unpacking.
ax_lines.append((line, x, y))
lines.append(ax_lines)
return lines
fig, axes = plt.subplots(2, 1, sharex=True, sharey=True)
lines = make_lines(axes)
setup_axes(axes)
return fig, axes, lines
def make_data():
for i in xrange(100):
print 'make_data():', i
data = dict()
for label in ('a', 'b', 'c'):
from random import random
data[label] = random()
yield (i + 1, data)
def update_lines(data, lines):
print 'update_lines():', data, lines
updated_lines = []
for ax_lines in lines:
for line, x, y in ax_lines:
label = line.get_label()
x.append(data[0])
y.append(data[1][label])
line.set_data(x, y)
updated_lines.append(line)
def main():
fig, axes, lines = make_subplots()
# Uncomment these 3 lines, and it works!
# new_data = make_data()
# for data in new_data:
# update_lines(data, lines)
FuncAnimation(fig=fig,
func=update_lines,
frames=make_data,
fargs=(lines,),
interval=10,
blit=False)
plt.show()
if __name__ == '__main__':
main()
1 个回答
14
(未记录的?)钩子
我在查看 matplotlib.animation.Animation
的源代码时,发现了 __init__()
函数中的这些代码:
# Clear the initial frame
self._init_draw()
# Instead of starting the event source now, we connect to the figure's
# draw_event, so that we only start once the figure has been drawn.
self._first_draw_id = fig.canvas.mpl_connect('draw_event', self._start)
听起来很熟悉...
到目前为止,这看起来没问题。调用 self._init_draw()
会立即绘制我的第一帧。然后,动画对象会连接到图形对象,并在图形显示之前等待,不会再绘制其他帧。
发现了!
关键在于:动画-对象。因为我并不打算在之后使用这个动画实例(比如说,绘制一个电影),所以我没有把它赋值给一个变量。实际上,我还被 pyflakes 警告,因为 局部变量 '...' 被赋值但从未使用
。
但是,由于所有功能都依赖于这个钩子,当画布最终显示时,我猜 Python 的垃圾回收机制已经把这个动画实例给清除了——因为它从来没有被赋值给变量——所以动画就无法启动了。
解决办法
只需把 FuncAnimation
实例赋值给一个变量,一切就能正常工作了!
anim = FuncAnimation(fig=fig,
func=update_lines,
frames=make_data,
fargs=(lines,),
interval=10,
blit=False)