Skip to content

Animations & Object Persistence #1656

Open
jakevdp opened this Issue Jan 13, 2013 · 6 comments

5 participants

@jakevdp
jakevdp commented Jan 13, 2013

Unlike other plotting operations, if you create an animation and do not store it in a persistent variable, it will fail.

So, for example,

import matplotlib.pyplot as plt
from matplotlib import animation

fig = plt.figure()

def animate(i):
   # ... do something here

anim = animation.FuncAnimation(fig, animate)
plt.show()

will work, but changing the final lines to

animation.FuncAnimation(fig, animate)
plt.show()

(i.e. not creating a variable to store the animation) leads to a quiet failure.

I think the problem is because the figure does not store a reference to the animation object, python's garbage collection removes it if it is not explicitly stored.
Probably the best way to address this would be to add an animation attribute to the Figure class, such that any animation will store a pointer to itself in its associated figure.

I think this is a pretty easy fix, but I'm just wondering if there are any other details to be aware of when adding something like this.

@WeatherGod
Matplotlib Developers member

This is known and @dopplershift and I have discussed this particular issue offline many times before. We have thought of your idea before, but I forget why we dismissed it. There are peculiarities/features of animation objects that doesn't quite lend itself to this approach. Do you remember the reasons, Ryan?

@dopplershift

If memory serves correctly (and I'm older now, so that's no guarantee), my aim was to avoid any modification of the rest of matplotlib to support this. A secondary "issue" is that a figure can have more than one animation, so the figure would likely need a list of animations. If @jakevdp's extensive use of the module is any indication, this module seems to be getting good uptake and the overall approach seems like it was the right one--I'm more inclined now to make modifications to the broader matplotlib code base if they make it even easier to make animations.

Mostly however, my objection was and still (somewhat) is code purity. I personally feel it's a little silly to do this just to avoid the (correct) code of having to save a reference to the object manually. For instance, if you create a Line2D instance by hand, it's not automatically added to an axes, and if you don't save a reference, POOF! Also, it seems odd for the figure to assume ownership of the animation when it doesn't touch it otherwise at all. However, I do recognize that this seems to be somewhat of a pain point, or at least a common gotcha, and should be addressed.

So I'm not really sure what I'm arguing here. There are no technical hurdles with adding an animations attribute to Figure instances that holds the list of current animations or with having the animation add itself to this list. There's a part of me that feels like symmetry with the rest of matplotlib dictates that we use a separate (simplfied?) API call to create the animation and add it (ala plot() or add_subplot()), instead of a self-registering class instance. However, it's not clear to me what that API would look like other than a thin, dumb wrapper around the animation constructor, which, IMO, is worse code than just having the class instance do it.

@jakevdp
jakevdp commented Jan 14, 2013

Interesting thoughts. From my perspective, it would seem much more natural to do something like

fig.add_animation(anim_func)

rather than the current

anim = animation(fig, anim_func)

The class method version would probably not be more than just a wrapper around the current function, but it would be more apparent that it's actually meant to add something to the figure class.

It would bring up some questions, though: for example, if you call savefig on a figure containing an animation, should it try to use a MovieWriter class automatically? Should it raise a warning? What if there are multiple animations? I'm not sure how that should most naturally be handled.

@dopplershift

I agree the former fits better with matplotlib's typical, minimal OOP API. However, we have two different animation classes (FuncAnimation and ArtistAnimation), so the API would end up as either:

fig.add_artist_animation(*args)
fig.add_func_animation(*args)

or

fig.add_animation(klass, *args)

I'm not particularly keen on either of those. I also agree about savefig(), but I'm disinclined to add to the list of options--at least as long as animations live on as integrated, but not really integrated.

What about:

fig.add_animation(FuncAnimation(*args))

?

@jakevdp
jakevdp commented Jan 16, 2013

I agree that all those options are a bit kludgy. Regarding the last option: because the FuncAnimation constructor needs to know about the figure it's attached to, you'd probably have to call it with something awkward like

fig.add_animation(FuncAnimation(fig, *args))

Maybe the simplest thing to do would be to keep the syntax of animations the same as what it is now, but simply add a line to the animation constructor which does something like

fig.animations.append(self)

That would maintain full backward compatibility, would be very clean, and would sufficiently address the problem of object persistence. As a bonus, you'd be able to introspect each figure and see any animations that are attached to it.

@BrenBarn

Just a note on the "code purity" thing. Creating an "isolated" Line2D is different because you don't specify the axes or figures when doing so. In the code animation.FuncAnimation(fig, animate), you are already specifying an association between the animation and a figure, so I don't see how it's impure for the figure to know about that relationship.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.