Can I change the interval of a previously created

2020-04-08 14:10发布

问题:

I'm trying to figure out if is there any way for me to change the interval of an existing matplotlib FuncAnimation. I want to be able to adjust the speed of the animation according to the user input.

I found a similar question How do I change the interval between frames (python)?, but since it got no answer I thought I would ask it anyway.

A minimal example of what I need and have is:

"""
Based on Matplotlib Animation Example

author: Jake Vanderplas
https://stackoverflow.com/questions/35658472/animating-a-moving-dot
"""
from matplotlib import pyplot as plt
from matplotlib import animation
import Tkinter as tk
import numpy as np

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg


class AnimationWindow(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.fig = plt.figure(0, figsize=(10, 10))

        self.anim = None

        self.speed = 2

        self.canvas = FigureCanvasTkAgg(self.fig, self)
        self.canvas.show()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        self.canvas.mpl_connect('resize_event', self.on_resize)

        self.bar = tk.Scale(self, from_=0.25, to=10, resolution=0.25, command=self.change_play_speed, orient=tk.HORIZONTAL)
        self.bar.pack(fill=tk.X)

    def start_animation(self):
        ax = plt.axes()

        self.x = np.arange(0, 2 * np.pi, 0.01)
        self.line, = ax.plot(self.x, np.sin(self.x))

        # The return needs to be assigned to a variable in order to prevent the cleaning by the GC
        self.anim = animation.FuncAnimation(self.fig, self.animation_update, frames=100,
                                            interval=100/self.speed, blit=True, repeat=False)

    def animation_update(self, i):
        self.line.set_ydata(np.sin(self.x + i / 10.0))  # update the data
        return self.line,

        return tuple(self.annotation)

    def change_play_speed(self, speed):
        self.speed = float(speed)

        # This works but I think somehow the previous animation remains
        #self.anim = animation.FuncAnimation(self.fig, self.animation_update, frames=100, interval=100/self.speed, blit=True, repeat=False)

    def on_resize(self, event):
        """This function runs when the window is resized.
         It's used to clear the previous points from the animation which remain after resizing the windows."""

        plt.cla()


def main():
    root = tk.Tk()

    rw = AnimationWindow(root)
    rw.pack()

    rw.start_animation()

    root.mainloop()

if __name__ == '__main__':
    main()

In the change speed function I have a commented solution to this problem. This solution presents two main problems: it is most likely very inefficient (I think); and I haven't figured out a way for me to delete the previous animation which results in flickering.

回答1:

I would not recomment to delete the animation. One option for more complex animations is of course to program them manually. Using a timer which repeatedly calls the update function is actually not much more code than creating the FuncAnimation.

However in this case the solution is very simple. Just change the interval of the underlying event_source:

def change_play_speed(self, speed):
    self.speed = float(speed)
    self.anim.event_source.interval = 100./self.speed