在matplotlib动画模块中管理动态绘图 [英] Managing dynamic plotting in matplotlib Animation module

查看:86
本文介绍了在matplotlib动画模块中管理动态绘图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个迭代绘制的图形,该图形允许跳到下一帧,停止它并返回到上一帧.

I would like to have an iteratively plotted graph that allows for skipping to the next frame, stopping it and coming back to a previous frame.

我看过matplotlib动画模块,如果有一种方法可以实现以前的帧功能(例如,按下某个键时向后运行动画几帧),那将是一个完美的选择

I have looked at matplotlib Animation module which would be perfect if there was a way to implement the previous frame functionality (like run Animation backwards for a few frames when a key is pressed)

这对这样的事情会很好:

It would be nice to something like this:

def update_frame(i, data):
    fig.set_data(data[i])

但是我可以明确地管理迭代器是增加还是减少.

but in a way that I could explicitly manage whether the i iterator increases or decreases.

在matplotlib中有没有办法做到这一点? 我应该寻找其他的python模块吗?

Is there a way to do that in matplotlib? Should I look for a different python module?

推荐答案

FuncAnimation类允许

The FuncAnimation class allows to supply a generator function to the frames argument. This function would be expected to yield a value that is supplied to the updating function for each step of the animantion.

FuncAnimation doc 状态:

The FuncAnimation doc states:

frames:可迭代,整数,生成器函数或无,可选 [..]
如果是生成器函数,则必须具有签名
def gen_function() -> obj:
在所有这些情况下,帧中的值都简单地传递到用户提供的func,因此可以是任何类型.

frames : iterable, int, generator function, or None, optional [..]
If a generator function, then must have the signature
def gen_function() -> obj:
In all of these cases, the values in frames is simply passed through to the user-supplied func and thus can be of any type.

我们现在可以创建一个生成器函数,该函数生成正向或反向整数,以便动画向前运行或向后 .要引导动画,我们可以使用matplotlib.widgets.Button s并向前创建一个单步或向后功能.这类似于我的答案关于循环浏览一组图像的问题.

We can now create a generator function which yields integers either in forward or in backward direction such that the animation runs forwards or backwards . To steer the animation, we might use matplotlib.widgets.Buttons and also create a one-step forward or backward functionality. This is similar to my answer to the question about looping through a set of images.

下面是一个名为Player的类,该类继承了FuncAnimation并将所有这些结合在一起,从而可以启动和停止动画.可以类似于FuncAnimation

The following is a class called Player which subclasses FuncAnimation and incoorporates all of this, allowing to start and stop the animation. It can be instantiated similarly to FuncAnimation,

ani = Player(fig, update, mini=0, maxi=10)

其中,update将是一个更新函数,需要一个整数作为输入,而minimaxi表示该函数可以使用的最小和最大数字.此类存储当前索引(self.i)的值,这样,如果停止或还原动画,它将在当前帧处重新开始.

where update would be an updating function, expecting an integer as input, and mini and maxi denote the minimal and maximal number that the function could use. This class stores the value of the current index (self.i), such that if the animation is stopped or reverted it will restart at the current frame.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import mpl_toolkits.axes_grid1
import matplotlib.widgets

class Player(FuncAnimation):
    def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
                 save_count=None, mini=0, maxi=100, pos=(0.125, 0.92), **kwargs):
        self.i = 0
        self.min=mini
        self.max=maxi
        self.runs = True
        self.forwards = True
        self.fig = fig
        self.func = func
        self.setup(pos)
        FuncAnimation.__init__(self,self.fig, self.func, frames=self.play(), 
                                           init_func=init_func, fargs=fargs,
                                           save_count=save_count, **kwargs )    

    def play(self):
        while self.runs:
            self.i = self.i+self.forwards-(not self.forwards)
            if self.i > self.min and self.i < self.max:
                yield self.i
            else:
                self.stop()
                yield self.i

    def start(self):
        self.runs=True
        self.event_source.start()

    def stop(self, event=None):
        self.runs = False
        self.event_source.stop()

    def forward(self, event=None):
        self.forwards = True
        self.start()
    def backward(self, event=None):
        self.forwards = False
        self.start()
    def oneforward(self, event=None):
        self.forwards = True
        self.onestep()
    def onebackward(self, event=None):
        self.forwards = False
        self.onestep()

    def onestep(self):
        if self.i > self.min and self.i < self.max:
            self.i = self.i+self.forwards-(not self.forwards)
        elif self.i == self.min and self.forwards:
            self.i+=1
        elif self.i == self.max and not self.forwards:
            self.i-=1
        self.func(self.i)
        self.fig.canvas.draw_idle()

    def setup(self, pos):
        playerax = self.fig.add_axes([pos[0],pos[1], 0.22, 0.04])
        divider = mpl_toolkits.axes_grid1.make_axes_locatable(playerax)
        bax = divider.append_axes("right", size="80%", pad=0.05)
        sax = divider.append_axes("right", size="80%", pad=0.05)
        fax = divider.append_axes("right", size="80%", pad=0.05)
        ofax = divider.append_axes("right", size="100%", pad=0.05)
        self.button_oneback = matplotlib.widgets.Button(playerax, label=ur'$\u29CF$')
        self.button_back = matplotlib.widgets.Button(bax, label=u'$\u25C0$')
        self.button_stop = matplotlib.widgets.Button(sax, label=u'$\u25A0$')
        self.button_forward = matplotlib.widgets.Button(fax, label=u'$\u25B6$')
        self.button_oneforward = matplotlib.widgets.Button(ofax, label=u'$\u29D0$')
        self.button_oneback.on_clicked(self.onebackward)
        self.button_back.on_clicked(self.backward)
        self.button_stop.on_clicked(self.stop)
        self.button_forward.on_clicked(self.forward)
        self.button_oneforward.on_clicked(self.oneforward)

### using this class is as easy as using FuncAnimation:            

fig, ax = plt.subplots()
x = np.linspace(0,6*np.pi, num=100)
y = np.sin(x)

ax.plot(x,y)
point, = ax.plot([],[], marker="o", color="crimson", ms=15)

def update(i):
    point.set_data(x[i],y[i])

ani = Player(fig, update, maxi=len(y)-1)

plt.show()

注意:这篇文章的编写方式并没有让它产生斑点.

Note: This hasn't been written in a way to allow for blitting.

这篇关于在matplotlib动画模块中管理动态绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆