0

When using imshow artist in FuncAnimation the first frame cannot be blitted away. It will always leave the first frame untouched, but other artists behave correctly. How can we fix this? Here is the code:

from matplotlib import pyplot as plt, animation, cm
def show_imshow_blit_bug(start=0, blit=True):
    pic = np.zeros((10, 10), dtype=float)
    x = np.arange(0, 2 * np.pi, 0.01)
    amp = (x.max() - x.min()) / 2

    fig, ax = plt.subplots()
    extent = [x.min(), x.max(), -amp, amp]
    _cm = cm.jet
    picf = pic.flat
    picf[0] = 1
    picf[-1] = 0.5
    imart = ax.imshow(pic,
                      origin='lower',
                      extent=extent,
                      cmap=_cm
                      )
    # imart.set_animated(True) # set or not has no effect
    line, = ax.plot(x, np.sin(x)*amp)
    line2, = ax.plot(x[[0, -1]], np.array([-amp, amp]), 'r')
    line2.set_animated(True) # this make line2 disappear, since updater does not return this artist
    line3, = ax.plot(x[[0, -1]], np.array([amp, -amp]), 'C1')
    #line3 will be keeped since _animated = False
    ax.set_xlim(x.min(), x.max())
    ax.set_ylim(-amp * 1.1, amp * 1.1)
    fig.tight_layout()

    def updater(fn):
        print(fn)
        np.random.shuffle(picf)
        imart.set_data(np.ma.masked_where(pic == 0, pic))
        line.set_ydata(np.sin(x + fn / 10.0)*amp)
        return (imart, line,) # Both artist will be in blit list

    nframe = range(start, 200)
    anima = animation.FuncAnimation(fig,
                            updater,
                            nframe,
                            interval=20,
                            blit=blit,
                            repeat=False
                            )
    plt.show()

I am aware there is a hack solution to set artists invisible in init_func. But this is a hack and I do not want to use init_func which is not compatible with the rest of codes.

Georgy
  • 12,464
  • 7
  • 65
  • 73
Wang
  • 7,250
  • 4
  • 35
  • 66

1 Answers1

0

The FuncAnimation documentation states

init_func : callable, optional

A function used to draw a clear frame. If not given, the results of drawing from the first item in the frames sequence will be used. This function will be called once before the first frame.)

So using init_func is not a hack or undesired - it is actually the intended usage.

You would initiate an empty image

imart = ax.imshow([[]], ...)

have an init function

def init():
    return imart,line,

and an updating function

def updater(fn):
    np.random.shuffle(picf)
    imart.set_data(np.ma.masked_where(pic == 0, pic))
    line.set_ydata(np.sin(x + fn / 10.0)*amp)
    return (imart, line,)

and instantiate FuncAnimation

anima = animation.FuncAnimation(fig,
                        updater,
                        nframe,
                        init_func=init,
                        interval=20,
                        blit=blit,
                        repeat=False
                        )
Community
  • 1
  • 1
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • This is a hack. Because you create an empty `imart`. If there is no `init_func`, the `_init_draw` will just call updater(0). If you look into the code, what `_init_draw` did is just register all artists returned by `init_func` with `set_animated(True)`. In theory, it should not leave any frame there. As I said this only failed with imshow. Other artists do not require an empty initial artists. – Wang Oct 06 '17 at 12:20
  • Not sure what the question is asking then. If you feel this is a bug, SO is not the right place for reporting it, go to the [matplotlib issue tracker](https://github.com/matplotlib/matplotlib/issues). But if you consider for a moment that there might be a good reason for images not having the _animated property translated into visibility, the above is a sensible solution. Or maybe you just restate what exactly you want to ask here. – ImportanceOfBeingErnest Oct 06 '17 at 13:20