<div dir="ltr"><div class="gmail_quote"><div dir="ltr"><div><div><div>Hi,<br><br></div>Out of my own needs I developed an animation class that is very similar to FuncAnimation but is drawn asynchronously (not that this couldn't be done with FuncAnimation, this is just a more direct path). Instead of trying to explain the differences off the bat I'll give a base example usage<br><br><div style="margin-left:40px">fig, ax = plt.subplots()<br>x = np.arange(0, 2*np.pi, 0.01)<br>line, = ax.plot(x, np.sin(x))<br><br>ani = AsyncAnimation(fig,interval=50)<br>fig.show() # starts the first 'draw' without entering the mpl mainloop<br><br>i = 0<br>while ani.update(): #update() flushes the animation's queue<br>    i += 1<br>    y = np.sin(x+i/10.0)<br>    ani.append_artist(line,(x,y))<br></div><br></div>This is the simplest example, where one frame is queued at a time and `update()` blocks execution until that frame has been rendered (so this is effectively the same as a FuncAnimation).<br><br>Aesthetically, the difference is that the animation code lives inside a loop instead of a function call. This removes the need to have the animation code pass variables back to the user or doing any global imports of variables which IMHO makes the application code cleaner and more self-contained. Personally, I find this a simpler and more intuitive way to write interactive animation applications. (Alternatively, writing application code inside of a class that uses FuncAnimation gives you similar benefits of cleaner code).<br><br></div>The other more significant difference is that having rendering being done asynchronously basically decouples the animation code from the application code, which <br><ol><li>Makes it trivial to execute the graphics rendering as a separate process</li><li>Gives the user more flexibility in their application code (you could imagine an application "queueing up" multiple frames at a time, allowing them to render in their own time).</li></ol>Anyway, here is the code as it stands now (most important are the `append_artist` and `update` functions; the rest of the code is very similar to the FuncAnimation class):<br><div><p style="margin-left:40px">class AsyncAnimation(Animation):<br>    """<br>    Makes an animation by reading from a user filled queue every *interval*<br>    milliseconds. User adds to the queue by calling *update_artists*.<br><br>    Note that data is _not_ copied before being put into the queue; if a reference<br>    is passed (such as a numpy ndarray) whatever data the reference is pointing<br>    to at draw time will be plotted.<br><br>    *init_func* is a function used to draw a clear frame. If not given, the<br>    results of drawing from the first item in the frames sequence will be<br>    used. This function will be called once before the first frame.<br><br>    *event_source* Default event source to trigger poll of the artist queue. By<br>    default this is a timer with an *interval* millisecond timeout.<br>    """<br>    def __init__(self,fig,init_func=None,event_source=None,interval=10,blit=True):<br>        self._data = deque()<br>        self._lag = 0<br>        self._queued = False<br><br>        self._fig = fig<br>        self._interval = interval<br>        <br>        self._init_func = init_func<br>        <br>        if event_source is None:<br>            event_source = fig.canvas.new_timer(interval=self._interval)<br><br>        Animation.__init__(self,fig,event_source=event_source,blit=blit)<br><br>    def new_frame_seq(self):<br>        return itertools.count()<br><br>    def _init_draw(self):<br>        if self._init_func is not None:<br>            self._drawn_artists = self._init_func()<br>            for a in self._drawn_artists:<br>                a.set_animated(self._blit)<br><br>    def _draw_next_frame(self, *args):<br>        # carry on if there's nothing to draw right now<br>        if self._data:<br>            Animation._draw_next_frame(self,*args)<br><br>    def _draw_frame(self,framedata):<br>        artdata = self._data.popleft()<br><br>        artists = []<br>        for (a,d) in artdata:<br>            artists.append(a)<br>            if d is not None: a.set_data(d)<br>            a.set_animated(self._blit)<br>        self._drawn_artists = artists<br><br>    def append_artist(self,artist,data=None):<br>        self._queue_artists((artist,data),)<br><br>    def extend_artists(self,artists):<br>        self._queue_artists(*artists)<br><br>    def _queue_artists(self,*artists):<br>        if len(self._data) and self._queued:<br>            self._data[-1] += artists<br>            return<br><br>        self._queued = True<br><br>        if len(self._data) > self._lag:<br>            warnings.warn("Artists queue is behind by %d" % len(self._data))<br>            self._lag = len(self._data)<br><br>        self._data.append(artists)<br><br>    def update(self,block=True):<br>        self._queued = False<br>        self._fig.canvas.flush_events()<br><br>        if block: <br>            while len(self._data) and self.event_source is not None: <br>                self._fig.canvas.flush_events()<br><br>        return self.event_source is not None</p><p><br></p><p>So, I've developed this code enough that it fills my needs, but it 
needs some work to add in all the expected matplotlib functionality. Is 
something that would be useful to people?</p></div></div>
</div><br></div>