Skip to content

Animations & Object Persistence #1656

Closed as not planned
Closed as not planned

Description

@jakevdp

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.

Activity

  1. WeatherGod commented on Jan 14, 2013

    @WeatherGod
    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?

  2. dopplershift commented on Jan 14, 2013

    @dopplershift
    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.

  3. jakevdp commented on Jan 14, 2013

    @jakevdp
    ContributorAuthor

    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.

  4. dopplershift commented on Jan 16, 2013

    @dopplershift
    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))
    

    ?

  5. jakevdp commented on Jan 16, 2013

    @jakevdp
    ContributorAuthor

    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.

  6. BrenBarn commented on Aug 27, 2014

    @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.

  7. petehuang commented on Jan 7, 2017

    @petehuang
    Contributor

    What's the latest on this?

  8. dopplershift commented on Jan 7, 2017

    @dopplershift
    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.

  9. modified the milestones: , on Oct 3, 2017
  10. added
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    on Oct 16, 2017
  11. 3 remaining items

  12. t-makaro commented on Aug 10, 2018

    @t-makaro
    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

  13. modified the milestones: v3.0, v3.1 on Aug 11, 2018
  14. modified the milestones: v3.1.0, v3.2.0 on Mar 18, 2019
  15. added and removed
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    on Aug 13, 2019
  16. anntzer commented on Aug 13, 2019

    @anntzer
    Contributor

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

  17. github-actions commented on Feb 28, 2023

    @github-actions

    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!

  18. jklymak commented on Feb 28, 2023

    @jklymak
    Member

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …Difficulty: Easyhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issuesstatus: needs comment/discussionneeds consensus on next steptopic: animation

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions