matplotlib动画FuncAnimation帧参数

Ric*_*ati 5 python animation iterable callable matplotlib

我通过调用使用matplotlib动画:

plot = animation.FuncAnimation(fig, update, frames=data_gen(a), init_func=init, interval=10, blit=True)
Run Code Online (Sandbox Code Playgroud)

这里,"a"是data_gen函数的初始值,如下所示:

data_gen(x)
    old_x = x
    while True:
        new_x = func(old_x)
        old_x = new_x
        yield new_x
Run Code Online (Sandbox Code Playgroud)

此代码的目的是让data_gen在每次更新动画图时为new_x生成新值.

但是......这恰巧发生了:

animation.py 在FuncAnimation类的init()方法中抛出错误.

此代码中出现此问题:

elif iterable(frames):
    self._iter_gen = lambda: iter(frames)
    self.save_count = len(frames)
Run Code Online (Sandbox Code Playgroud)

错误是"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
Run Code Online (Sandbox Code Playgroud)

我不确定为什么我的data_gen不是collections.Callable.如果是,则len(帧)永远不会发生.

对于我应该做些什么的任何建议将不胜感激!

tac*_*ell 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 = []
Run Code Online (Sandbox Code Playgroud)

我们现在有一个函数monkey_patch_init(我声称)是固定代码。我们现在只需__init__用此函数替换有缺陷的函数:

matplotlib.animation.FuncAnimation.__init__ = monkey_patch_init
Run Code Online (Sandbox Code Playgroud)

你的动画应该可以工作。

ani = animation.FuncAnimation(fig, update, frames=data_gen(a), init_func=init, interval=10, blit=True)
Run Code Online (Sandbox Code Playgroud)

作为旁注,不要用作plot变量名称。很多人批量导入pyplot到他们的名称空间(例如 by ipython --pylab)中,其中包含plot-> matplotlib.pyplot.plot->plt.gca().plot因此,这使得您的代码更有可能让某人感到困惑。