我通过调用:
使用matplotlib动画plot = animation.FuncAnimation(fig, update, frames=data_gen(a), init_func=init, interval=10, blit=True)
这里,“a”是data_gen函数的初始值,如下所示:
data_gen(x)
old_x = x
while True:
new_x = func(old_x)
old_x = new_x
yield new_x
此代码的目的是让data_gen在每次更新动画图时为new_x生成一个新值。
但是......这恰巧发生了:
animation.py在FuncAnimation类的 init ()方法中抛出错误。
此代码中出现问题:
elif iterable(frames):
self._iter_gen = lambda: iter(frames)
self.save_count = len(frames)
错误是“TypeError:类型'生成器'的对象没有len()”
看起来data_gen是可迭代的,但它没有len()。
以下是FuncAnimation类中 init ()方法的更多代码:
# Set up a function that creates a new iterable when needed. If nothing
# is passed in for frames, just use itertools.count, which will just
# keep counting from 0. A callable passed in for frames is assumed to
# be a generator. An iterable will be used as is, and anything else
# will be treated as a number of frames.
if frames is None:
self._iter_gen = itertools.count
elif isinstance(frames, collections.Callable):
self._iter_gen = frames
elif iterable(frames):
self._iter_gen = lambda: iter(frames)
self.save_count = len(frames)
else:
self._iter_gen = lambda: iter(list(range(frames)))
self.save_count = frames
我不确定为什么我的data_gen不是collections.Callable。如果是,则len(帧)永远不会发生。
对于我应该做些什么的任何建议都将不胜感激!
答案 0 :(得分:2)
解决方案是A)预先生成所有数据并将其推送到列表中(如果你的帧数确实有限),即data_list = list(data_gen)
B)从我的分支机构安装来源解决了这个问题:PR #2634或C)猴子补丁matplotlib,它只是在运行时使用固定代码替换安装中的错误代码
C)是最有趣的;)
# copied directly from the proposed fix
def monkey_patch_init(self, fig, func, frames=None, init_func=None, fargs=None,
save_count=None, **kwargs):
if fargs:
self._args = fargs
else:
self._args = ()
self._func = func
# Amount of framedata to keep around for saving movies. This is only
# used if we don't know how many frames there will be: in the case
# of no generator or in the case of a callable.
self.save_count = save_count
# Set up a function that creates a new iterable when needed. If nothing
# is passed in for frames, just use itertools.count, which will just
# keep counting from 0. A callable passed in for frames is assumed to
# be a generator. An iterable will be used as is, and anything else
# will be treated as a number of frames.
if frames is None:
self._iter_gen = itertools.count
elif six.callable(frames):
self._iter_gen = frames
elif iterable(frames):
self._iter_gen = lambda: iter(frames)
if hasattr(frames, '__len__'):
self.save_count = len(frames)
else:
self._iter_gen = lambda: xrange(frames).__iter__()
self.save_count = frames
# If we're passed in and using the default, set it to 100.
if self.save_count is None:
self.save_count = 100
self._init_func = init_func
# Needs to be initialized so the draw functions work without checking
self._save_seq = []
TimedAnimation.__init__(self, fig, **kwargs)
# Need to reset the saved seq, since right now it will contain data
# for a single frame from init, which is not what we want.
self._save_seq = []
我们现在有一个函数monkey_patch_init
,它是(我声称的)固定代码。我们现在只用这个函数替换bug __init__
函数:
matplotlib.animation.FuncAnimation.__init__ = monkey_patch_init
,你的动画应该有用。
ani = animation.FuncAnimation(fig, update, frames=data_gen(a), init_func=init, interval=10, blit=True)
作为旁注,请勿使用plot
作为变量名称。很多人将pyplot
批量导入其名称空间(例如ipython --pylab
),其中plot
- > matplotlib.pyplot.plot
- > plt.gca().plot
因此它使得代码更容易让某人感到困惑。