Skip to content

Animations & Object Persistence #1656

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jakevdp opened this issue Jan 13, 2013 · 12 comments
Closed

Animations & Object Persistence #1656

jakevdp opened this issue Jan 13, 2013 · 12 comments
Labels
API: consistency Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues status: needs comment/discussion needs consensus on next step topic: animation

Comments

@jakevdp
Copy link
Contributor

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
Copy link
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
Copy link
Contributor

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
Copy link
Contributor Author

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
Copy link
Contributor

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
Copy link
Contributor Author

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
Copy link

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.

@petehuang
Copy link
Contributor

What's the latest on this?

@dopplershift
Copy link
Contributor

Looking at it with really fresh eyes (2 years later), I think @jakevdp 's last suggestion, of just adding to a fig.animations list within the constructor (should just be able to go in the Animation base class) is probably the way to go to eliminate a common gotcha for animations.

My only minor concern here is that we're taking a one-way coupling (Animation knows about Figure), and turning it into a two-way coupling (now also Figure knows about Animation). It's only one small list--but statements like that are how we ended up with the highly coupled, somewhat cohesive, ungodly mess that is much of matplotlib's (highly functional) codebase.

(For the record, I still don't consider this a problem, since FuncAnimation and co. are classes; nowhere in Python do we create an instance of a class and expect it to go on living if we don't assign a name to it.)

I'm not volunteering to do it, but adding the animations list to the Figure constructor and calling append in the Animation constructor seems straightforward enough to be beginner ready.

@dopplershift dopplershift added Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues new-contributor-friendly labels Jan 7, 2017
@tacaswell tacaswell modified the milestones: 2.1 (next point release), 2.2 (next next feature release) Oct 3, 2017
@tacaswell tacaswell added the Good first issue Open a pull request against these issues if there are no active ones! label Oct 16, 2017
@anntzer anntzer modified the milestones: needs sorting, v3.0 Feb 16, 2018
@t-makaro
Copy link
Contributor

So, I noticed an interesting use case for this in writing tests.

Currently, when doing image comparison tests. A test function is written to make a plot, and a decorator grabs that figure and saves it as an image before comparing it. This figures are grabbed by these lines. If the figure had knowledge of the animation, then it would be easy to grab the animation and save it for comparison. Alternatively, the test function could return the animation so that the decorator could pick it up. #11839

@tacaswell tacaswell modified the milestones: v3.0, v3.1 Aug 11, 2018
@tacaswell tacaswell modified the milestones: v3.1.0, v3.2.0 Mar 18, 2019
@anntzer anntzer added status: needs comment/discussion needs consensus on next step and removed Good first issue Open a pull request against these issues if there are no active ones! labels Aug 13, 2019
@anntzer
Copy link
Contributor

anntzer commented Aug 13, 2019

Untagged "good first issue", given that it's really a design problem now (and we already rejected two PRs implementing the "obvious" solution).

@tacaswell tacaswell removed this from the v3.2.0 milestone Sep 10, 2019
@github-actions
Copy link

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Feb 28, 2023
@jklymak
Copy link
Member

jklymak commented Feb 28, 2023

I think we've had this conversation a few times in favour of asking people to keep their animations persistent. Yes, people are still sometimes confused about this, but the docs have a lot of pointers that explain the right way to do this, so I think it's OK to close this.

@jklymak jklymak closed this as not planned Won't fix, can't repro, duplicate, stale Feb 28, 2023
@rcomer rcomer removed the status: inactive Marked by the “Stale” Github Action label Mar 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API: consistency Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues status: needs comment/discussion needs consensus on next step topic: animation
Projects
None yet