1

Is there some way to call matplotlib.animation.FuncAnimation more than once in the same code? I have a list of numpy arrays. The arrays each contain a different number of coordinates. I want to loop through the list and animate plotting the points in each array.

Is there anything I can do to achieve these two different scenarios: 1. Retain the last frame from the previous animation and start a new animation over that. 2. Get rid of the last frame from the previous animation, and start a fresh animation, but keep the same layout set at the beginning of the code (including background plots).

When I try to use FuncAnimation in a for loop like below, only the first array gets animated. In between each array, I also need to do some other stuff, so I can't just get rid of the for loop and merge the arrays to be animated all together.

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

mylines = [np.array([[1,2], [3,4]]), np.array([[5,6], [7,8], [9,10]])]

fig, ax = plt.subplots()

ax.set_xlim([-10, 10])
ax.set_ylim([-10, 10])

# There's some code here to set the layout (some background plots etc)
# I omitted it in this question


def update_plot(i, data, myplot):
    myplot.set_data(data[:i, 0], data[:i, 1])
    return myplot,

myplot, = ax.plot([], [], '.', markersize=5)

for k in xrange(len(mylines)):
    data = mylines[k]
    numframes = data.shape[0]+1
    delay = 500

    ani = animation.FuncAnimation(fig, update_plot, frames=numframes,
                                  fargs=(data, myplot), interval=delay,
                                  blit=True, repeat=False)
    plt.show()


    # Do some more stuff in the for loop here, that's not related to
    # animations but related to the current array


    # Then I want to retain the last frame from the animation, and start a new
    # animation on top of it. Also, what should I do if I want to erase the
    # last frame instead (erase only the points plotted by the FuncAnimation,
    # but keep the layout I set at the beginning)

*Edited code because it had some syntax errors

Also tried following this question's solution:

ani = []

for k in xrange(len(mylines)):
    data = mylines[k]
    numframes = data.shape[0]+1
    delay = 100

    ani.append(animation.FuncAnimation(fig, update_plot, frames=numframes,
                                  fargs=(data, myplot), interval=delay,
                                  blit=True, repeat=False))
plt.show()

but it plots all the arrays all at once, with some weird flickering.

vegzh
  • 61
  • 2
  • 4
  • What exactly is the purpose of the animation? Maybe you can explain what should happen step by step? – ImportanceOfBeingErnest Jul 30 '17 at 11:39
  • @ImportanceOfBeingErnest I'm not sure how I can explain it more clearly but I'll try: I have a list of numpy arrays (this list is called mylines in the code I posted in the question). Each array (in the mylines list) contains coordinates for points that I want to plot... there are a different number of points in each array. I want to loop through the list and animate plotting the points in each array (every frame adds a new point to the figure). – vegzh Jul 30 '17 at 23:34
  • @ImportanceOfBeingErnest When I try the code I typed in the question, it only animates the plotting of the first array in the mylines list, then it just stops. I tried to debug the code using breakpoints, and it does successfully go through the for loop... it just never plays the animations for the next arrays, even though FuncAnimation and plt.show() is being called every time it loops. – vegzh Jul 30 '17 at 23:35
  • @ImportanceOfBeingErnest So I'm wondering how I can run an animation (add points to the figure one by one), have the last frame of the animation stay on the figure (when all the points are plotted, keep the points on the graph), and run a new different animation on top of that frame (after the first animation completes). As a different problem, I am also wondering if it is possible to clear the points plotted by the first animation but retain the layout (axis limits, background plots etc.) set BEFORE the first animation runs, and then run a new animation after the first animation finishes – vegzh Jul 30 '17 at 23:41
  • @ImportanceOfBeingErnest I also tried animating the individual arrays with a for loop instead of FuncAnimation, but it was too slow. – vegzh Jul 30 '17 at 23:43
  • All of those explanations should be part of the question, comments are not meant to be used like that. Now let me ask this differently: What is the difference between having two subsequent animations of 2 and 3 rows of an array respectively compared to one single animation of an array with 5 rows? – ImportanceOfBeingErnest Jul 31 '17 at 00:55
  • @ImportanceOfBeingErnest Sorry, I'll try editing the extra details into the question next time! I think the difference is: having two separate/subsequent animations would allow me to implement some other code BETWEEN each animation, while a single animation would just produce one "video"? As I tried to explain in the question, if I combined everything into one animation, I don't know how to do other tasks between each array plot. – vegzh Jul 31 '17 at 01:39

1 Answers1

0

Think of the FuncAnimation as a timer. It will simply call the function provided to it at a given rate with the given arguments. Rather than having two timers, for which you would need to manage when each of them starts and finishes, you would use the function that is called for managing the animation flow.

For example, you could set a framenumber at which something should happen during the animation:

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

mylines = [np.array([[1,2], [3,4]]), np.array([[5,6], [7,8], [9,10]])]

fig, ax = plt.subplots()

ax.set_xlim([-10, 10])
ax.set_ylim([-10, 10])

# There's some code here to set the layout (some background plots etc)
# I omitted it in this question

myplot, = ax.plot([], [], '.', markersize=5)


delay = 500
breaking = 1 

def update_plot(i, data, myplot):
    myplot.set_data(data[:i, 0], data[:i, 1])
    if i == breaking:
        # do some other stuff
        print("breaking")
        myplot.set_color("red")
    return myplot,

data = np.append(mylines[0],mylines[1], axis=0)

ani = animation.FuncAnimation(fig, update_plot, frames=data.shape[0],
                              fargs=(data, myplot), interval=delay,
                              blit=True, repeat=False)
plt.show()

Of course this can be arbitrarily complicated. See for example this question: Managing dynamic plotting in matplotlib Animation module, where the direction of the animation is reversed on user input. However, it still uses one single FuncAnimation.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712