Unable to make FuncAnimation work in a Class

Question:

I am having trouble with making FuncAnimation work in this code given below

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib notebook


class Simulator:
    def __init__(self, timesteps = 1000, ):
        self.simulation_time = np.linspace(0, timesteps, 10*(timesteps)+1)
        self._theta = np.arange(0,100, 10*(timesteps)+1)

    def init_plot(self):
        self._p1, = self.axes.plot(self.simulation_time[0], self._theta[0])
        return self.fig,

    def update_plot(self, i):
        self._p1.set_data(self.simulation_time[:i], self._theta[:i])
        return self.fig,

    def start_simulation(self):

        self.fig, self.axes = plt.subplots()
        ani=FuncAnimation(fig=self.fig, func=self.update_plot, init_func=self.init_plot,
                          interval=5, blit=True)
        plt.show()


simHandler = Simulator()
simHandler.start_simulation()

All i see after running this code is a blank screen..
enter image description here
But when i run similar code outside the Class as simple functions, the animation runs, any idea as to how to fix this?

Asked By: Dravidian

||

Answers:

There are several problem with your code.

The first and most important is that (as per the documentation):

it is critical to keep a reference to the instance object. The
animation is advanced by a timer (typically from the host GUI
framework) which the Animation object holds the only reference to. If
you do not hold a reference to the Animation object, it (and hence the
timers), will be garbage collected which will stop the animation.

That means that your class need to keep/return a reference to the animation, otherwise the animation will stop immediately.

The second issue is that, even if you do that, you need to update the xlims and ylims, as those are not scaled automatically when you use Line2D.set_data().

Finally, update_plot() should return a list of updated artists, not the reference to the figure (although that is only necessary if you are using blit=True)

Corrected code:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib notebook


class Simulator:
    def __init__(self, timesteps = 1000, ):
        self.simulation_time = np.linspace(0, timesteps, 10*(timesteps)+1)
        self._theta = np.sin(self.simulation_time) # I've changed this so one could see the plot being drawn

    def init_plot(self):
        self._p1, = self.axes.plot(self.simulation_time[0], self._theta[0])
        return self.fig,

    def update_plot(self, i):
        self._p1.set_data(self.simulation_time[:i], self._theta[:i])
        self.axes.set_xlim(right=self.simulation_time[i])
        return self._p1,

    def start_simulation(self):
        self.fig, self.axes = plt.subplots()
        self.ani = FuncAnimation(fig=self.fig, func=self.update_plot, init_func=self.init_plot,
                          interval=5, blit=True)


simHandler = Simulator()
simHandler.start_simulation()
Answered By: Diziet Asahi

To keep the code as suggested by Diziet Asahi running, following small adjustment where neccessary:

  1. remove the init_func from the FuncAnimation-call
  2. init the plot within the start_simulation-procedure
  3. minor changes to ‘update_plot’ in order to adjust the limits

(and furthermore remove the %matplotlib notebook statement)

the final result (and working for me) is:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

class Simulator:
    def __init__(self, timesteps = 1000, ):
        self.simulation_time = np.linspace(0, timesteps, 10*(timesteps)+1)
        self._theta = np.sin(self.simulation_time) # I've changed this so one could see the plot being drawn
    
    def update_plot(self, i):
        self._p1.set_data(self.simulation_time[:i], self._theta[:i])
        self.fig.gca().relim()
        self.fig.gca().autoscale_view() 
        return self._p1,

    def start_simulation(self):
        self.fig, self.axes = plt.subplots()
        self._p1, = self.axes.plot(self.simulation_time[0],self._theta[0])
        self.ani = FuncAnimation(fig=self.fig,func=self.update_plot,interval=5, blit=True)


simHandler = Simulator()
simHandler.start_simulation()
Answered By: Aroc
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.