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..
But when i run similar code outside the Class as simple functions, the animation runs, any idea as to how to fix this?
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()
To keep the code as suggested by Diziet Asahi running, following small adjustment where neccessary:
- remove the
init_func
from the FuncAnimation
-call
- init the plot within the
start_simulation
-procedure
- 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()
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..
But when i run similar code outside the Class as simple functions, the animation runs, any idea as to how to fix this?
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()
To keep the code as suggested by Diziet Asahi running, following small adjustment where neccessary:
- remove the
init_func
from theFuncAnimation
-call - init the plot within the
start_simulation
-procedure - 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()