From 9bf65505f9d1f4d48a03b17492afe11b32938db0 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 23:03:33 -0400 Subject: [PATCH 1/8] FIX: use draw_idle instead of draw Using draw was triggering infinite recursion, using draw_idle does not. --- lib/matplotlib/animation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index b3c946225ec5..f51d2fcfd347 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -1041,7 +1041,7 @@ def _init_draw(self): # Flush the needed axes for fig in figs: - fig.canvas.draw() + fig.canvas.draw_idle() def _pre_draw(self, framedata, blit): ''' From f25acff83caabb808fdb656806e2e7492be319f1 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Tue, 4 Aug 2015 09:37:29 -0600 Subject: [PATCH 2/8] Fix infinite recursion. This was introduced by #4800. We need to disconnect the initial draw callback in _start() before doing _init_draw(), otherwise, we end up back in _start() when the draw_event fires. Also go ahead and remove the call to _init_draw() in __init__(), since _start() will handle it. --- lib/matplotlib/animation.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index f51d2fcfd347..7260e18e10ab 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -590,9 +590,6 @@ def __init__(self, fig, event_source=None, blit=False): self.frame_seq = self.new_frame_seq() self.event_source = event_source - # Clear the initial frame - self._init_draw() - # Instead of starting the event source now, we connect to the figure's # draw_event, so that we only start once the figure has been drawn. self._first_draw_id = fig.canvas.mpl_connect('draw_event', self._start) @@ -609,15 +606,18 @@ def _start(self, *args): Starts interactive animation. Adds the draw frame command to the GUI handler, calls show to start the event loop. ''' - # On start, we add our callback for stepping the animation and - # actually start the event_source. We also disconnect _start - # from the draw_events - self.event_source.add_callback(self._step) - self._init_draw() - self.event_source.start() + # First disconnect our draw event handler self._fig.canvas.mpl_disconnect(self._first_draw_id) self._first_draw_id = None # So we can check on save + # Now do any initial draw + self._init_draw() + + # Add our callback for stepping the animation and + # actually start the event_source. + self.event_source.add_callback(self._step) + self.event_source.start() + def _stop(self, *args): # On stop we disconnect all of our events. if self._blit: From 33da7a56d42a1c6ef03c715c44789014334ca3d7 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 23:25:57 -0400 Subject: [PATCH 3/8] FIX: not all init/draw function return artists For blitted animations in is essential to return a list of the animated artists. However, is other cases you can get away with out returning such a list. In the case of blitting it is important to make sure that the animation flags on the animated artist are set correctly, t his commit makes sure that we check that we actually got an iterable before trying to iterate over it. --- lib/matplotlib/animation.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 7260e18e10ab..640636e1196e 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -1156,8 +1156,9 @@ def _init_draw(self): self._draw_frame(next(self.new_frame_seq())) else: self._drawn_artists = self._init_func() - for a in self._drawn_artists: - a.set_animated(self._blit) + if iterable(self._drawn_artists): + for a in self._drawn_artists: + a.set_animated(self._blit) def _draw_frame(self, framedata): # Save the data for potential saving of movies. @@ -1170,5 +1171,6 @@ def _draw_frame(self, framedata): # Call the func with framedata and args. If blitting is desired, # func needs to return a sequence of any artists that were modified. self._drawn_artists = self._func(framedata, *self._args) - for a in self._drawn_artists: - a.set_animated(self._blit) + if iterable(self._drawn_artists): + for a in self._drawn_artists: + a.set_animated(self._blit) From dd1d72771b0aacda91f4e4993c9668aa739060dc Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 23:37:01 -0400 Subject: [PATCH 4/8] MNT: be cautious about pulling from the bg_cache There are race conditions where _blit_clear may be called before the first draw (which is what will grab the backgrounds) --- lib/matplotlib/animation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 640636e1196e..f4ad34e780c9 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -859,7 +859,11 @@ def _blit_clear(self, artists, bg_cache): # cache and restore. axes = set(a.axes for a in artists) for a in axes: - a.figure.canvas.restore_region(bg_cache[a]) + try: + a.figure.canvas.restore_region(bg_cache[a]) + except KeyError: + # something has happened out of order? + pass def _setup_blit(self): # Setting up the blit requires: a cache of the background for the From ebfe80d427586f6b5ea9d570b69c89488f7c2788 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 23:45:39 -0400 Subject: [PATCH 5/8] MNT: change cmap to viridis + animate=True flag --- examples/animation/dynamic_image.py | 2 +- examples/animation/dynamic_image2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/animation/dynamic_image.py b/examples/animation/dynamic_image.py index 247bbc031ad9..fd07c36e7bb4 100644 --- a/examples/animation/dynamic_image.py +++ b/examples/animation/dynamic_image.py @@ -15,7 +15,7 @@ def f(x, y): x = np.linspace(0, 2 * np.pi, 120) y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) -im = plt.imshow(f(x, y), cmap=plt.get_cmap('jet')) +im = plt.imshow(f(x, y), cmap=plt.get_cmap('viridis'), animated=True) def updatefig(*args): diff --git a/examples/animation/dynamic_image2.py b/examples/animation/dynamic_image2.py index c4a29b3d00aa..b15723081020 100644 --- a/examples/animation/dynamic_image2.py +++ b/examples/animation/dynamic_image2.py @@ -21,7 +21,7 @@ def f(x, y): for i in range(60): x += np.pi / 15. y += np.pi / 20. - im = plt.imshow(f(x, y)) + im = plt.imshow(f(x, y), cmap='viridis', animated=True) ims.append([im]) ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, From cd0fe36dbdd8d2aae261a020bac58cdf62067028 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 23:50:33 -0400 Subject: [PATCH 6/8] MNT: make histogram animation use blitting --- examples/animation/histogram.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/animation/histogram.py b/examples/animation/histogram.py index a8c4c13b8cf4..1ca8c35ac5e5 100644 --- a/examples/animation/histogram.py +++ b/examples/animation/histogram.py @@ -58,6 +58,7 @@ def animate(i): top = bottom + n verts[1::5, 1] = top verts[2::5, 1] = top + return [patch, ] -ani = animation.FuncAnimation(fig, animate, 100, repeat=False) +ani = animation.FuncAnimation(fig, animate, 100, repeat=False, blit=True) plt.show() From e7393d9d6c23485bbdc6cb6e9c6bf4e587909b7e Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 27 Aug 2015 22:43:30 -0400 Subject: [PATCH 7/8] MNT: Simplify logic about when to force animated Only attempt to force `set_animated(True)` when blitting is enabled. --- lib/matplotlib/animation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index f4ad34e780c9..524f01028a21 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -1160,7 +1160,7 @@ def _init_draw(self): self._draw_frame(next(self.new_frame_seq())) else: self._drawn_artists = self._init_func() - if iterable(self._drawn_artists): + if self._blit: for a in self._drawn_artists: a.set_animated(self._blit) @@ -1175,6 +1175,6 @@ def _draw_frame(self, framedata): # Call the func with framedata and args. If blitting is desired, # func needs to return a sequence of any artists that were modified. self._drawn_artists = self._func(framedata, *self._args) - if iterable(self._drawn_artists): + if self._blit: for a in self._drawn_artists: a.set_animated(self._blit) From ea96ac8856e5ada4d374ef4f6be5860bc5700496 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 27 Aug 2015 22:59:25 -0400 Subject: [PATCH 8/8] MNT: backout poorly thought out try/except --- lib/matplotlib/animation.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 524f01028a21..6b1e05870196 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -859,11 +859,7 @@ def _blit_clear(self, artists, bg_cache): # cache and restore. axes = set(a.axes for a in artists) for a in axes: - try: - a.figure.canvas.restore_region(bg_cache[a]) - except KeyError: - # something has happened out of order? - pass + a.figure.canvas.restore_region(bg_cache[a]) def _setup_blit(self): # Setting up the blit requires: a cache of the background for the