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

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

问题描述

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

我看过 matplotlib Animation 模块,如果有办法实现前一帧的功能(比如按下一个键时向后运行几帧动画),这将是完美的

这样的事情会很好:

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

但以一种我可以明确管理 i 迭代器是增加还是减少的方式.

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

解决方案

FuncAnimation 类允许 或向后 .为了控制动画,我们可以使用 matplotlib.widgets.Buttons 并创建一个向前的 或向后 功能.这类似于

<小时>

注意:这不是以允许 blitting 的方式编写的.

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.

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.

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

解决方案

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.

The FuncAnimation doc states:

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.

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.

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)

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天全站免登陆