Search code examples
pythonanimationmatplotlibblit

matplotlib image animation not displayed if all entries in first frame are identical


I can't seem to get a TimedAnimation animation of an image to display anything if all entries in the initial frame contain the same value. For example, the following doesn't display anything if the indicated line remains commented out. Changing the first frame to contain np.ones(self.shape) also doesn't have any effect.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as anim

class MyAnimation(anim.TimedAnimation):
    def __init__(self):
        fig = plt.figure()
        self.shape = (10, 10)
        data = np.zeros(self.shape)
        # data.flat[0] = 1 # uncomment this to get the animation to display         
        self.ax = plt.imshow(data)
        super(self.__class__, self).__init__(fig, interval=50, blit=True)

    def _draw_frame(self, framedata):
        data = np.random.rand(*self.shape)
        self.ax.set_data(data)

    def new_frame_seq(self):
        return iter(range(100))

ani = MyAnimation()

Turning off blitting doesn't seem to have any effect. Changing the backend doesn't seem to make any difference either; I tried Qt4Agg and nbagg (the latter via Jupyter notebook 4.1.0) with the same results. I'm using numpy 1.10.4 and matplotlib 1.5.1 with Python 2.7.11.

Is the above behavior expected? If not, is there something I should be doing to get the animation to display when the first frame is a blank or solid image?


Solution

  • Setting the data does not recompute the color limits. In the case of all of the input values being the same the min/max are auto-scaled to the same value, hence you never see the data be update. You can either explicitly set the limits on init

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as anim
    
    class MyAnimation(anim.TimedAnimation):
        def __init__(self, ax=None):
            if ax is None:
                fig, ax = plt.subplots()
    
            self.shape = (10, 10)
            data = np.zeros(self.shape)
            self.im = ax.imshow(data, vmin=0, vmax=1)
            super(self.__class__, self).__init__(ax.figure, interval=50, blit=True)
    
        def _draw_frame(self, framedata):
            data = np.random.rand(*self.shape)
            self.im.set_data(data)
    
        def new_frame_seq(self):
            return iter(range(100))
    
    ani = MyAnimation()
    

    or use self.im.set_clim in the _draw_frame method.

    I am also not sure that sub-classing TimedAnimation is the easiest way to do what ever you are trying to do (FuncAnimation is pretty flexible).