Matplotlib animation; animate in push mode, not with a call back function

Question:

I want to create a matplotlib animation, but instead of having matplotlib call me I want to call matplotlib. For example I want to do this:

from random import random
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

def update(frame):
    plt.scatter(random(),random())

fig, ax = plt.subplots()
ani = FuncAnimation(fig, update, interval=340, repeat=True)
ani.save("my.mov", "avconv")

Like this:

def update():
    plt.scatter(random(),random())

fig, ax = plt.subplots()
ani = MadeUpSubClassPassiveAnimation(fig)

while True:
  update()
  ani.update()
  # do other stuff ...

ani.save("my.mov", "avconv") 

I realize I could drive a live plot like this:

def update():
  plt.scatter(x, y)
  plt.pause(0.01)

fig, ax = plt.subplots()
plt.ion()

while True:
  update()
  time.sleep(1)

But AFAIK I need to use Animation for the save() functionality. So, is it possible to drive Animation rather than have it drive me? If so how?

Asked By: spinkus

||

Answers:

An Animation is run when being saved. This means that the animation needs to reproducibly give the same result when being run twice (once for saving, once for showing). In other words, the animation needs to be defined in terms of successive frames. With this requirement, any animation can be constructed using a callback on either a function (FuncAnimation) or on a list of frames (ArtistAnimation).

The example from the question could be done with an ArtistAnimation (in order not to have different random numbers for the saved and the shown animation, respectively):

from random import random

import matplotlib.animation
import matplotlib.pyplot as plt

def update(frame: int) -> list[matplotlib.artist.Artist]:
    sc = ax.scatter(random(), random())
    return [sc]

fig, ax = plt.subplots()

artists = []

for i in range(10):
    sc = update(i)
    artists.append(sc)

# If you want previous plots to be present in all frames, add:
# artists = [[j[0] for j in artists[:i+1]] for i in range(len(artists))]
  
ani = matplotlib.animation.ArtistAnimation(fig, artists, interval=100)
ani.save(__file__ + ".gif", writer="imagemagick") 
plt.show()
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.