Dravidian
Dravidian

Reputation: 9955

Unable to make FuncAnimation work in a Class

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?

Upvotes: 0

Views: 1067

Answers (2)

Aroc
Aroc

Reputation: 1273

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()

Upvotes: 0

Diziet Asahi
Diziet Asahi

Reputation: 40747

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()

Upvotes: 1

Related Questions