@@ -1429,6 +1429,12 @@ def __init__(self, width, height, dpi):
14291429 self .axes = self .fig .add_subplot (111 )
14301430 self .axes .set (xlabel = "Time (s)" , ylabel = "Activation (AU)" )
14311431 self .manager = None
1432+ # Artists that are redrawn on their own (see `add_blit_artist`), the
1433+ # background they are drawn onto, and the draw_event callback id that
1434+ # keeps that background up to date.
1435+ self ._blit_artists = list ()
1436+ self ._blit_background = None
1437+ self ._blit_cid = None
14321438
14331439 def _connect (self ):
14341440 for event in ("button_press" , "motion_notify" ) + self ._extra_events :
@@ -1444,10 +1450,85 @@ def plot(self, x, y, label, update=True, **kwargs):
14441450 def plot_time_line (self , x , label , update = True , ** kwargs ):
14451451 """Plot the vertical line."""
14461452 line = self .axes .axvline (x , label = label , ** kwargs )
1453+ self .add_blit_artist (line )
14471454 if update :
14481455 self .update_plot ()
14491456 return line
14501457
1458+ def add_blit_artist (self , artist ):
1459+ """Mark an artist as fast-updating, to be drawn by :meth:`update_blit_artists`.
1460+
1461+ Such an artist is excluded from the canvas background, so that moving it
1462+ (e.g. the time line, or a label that travels with it) costs a blit of the
1463+ cached background rather than a full redraw of the figure.
1464+
1465+ Parameters
1466+ ----------
1467+ artist : instance of matplotlib.artist.Artist
1468+ The artist to draw separately. Must live in this canvas's axes: an
1469+ artist added to the figure itself would be left out of saved images,
1470+ because Matplotlib only exempts *Axes* children from the rule that
1471+ animated artists are not drawn (see ``_AxesBase.draw``).
1472+ """
1473+ if not self .canvas .supports_blit : # e.g. ipympl in a notebook
1474+ return
1475+ if artist .axes is not self .axes :
1476+ raise RuntimeError (
1477+ f"{ artist !r} must be an artist of this canvas's axes to be drawn "
1478+ "separately, got one in " + repr (artist .axes )
1479+ )
1480+ if artist in self ._blit_artists :
1481+ return
1482+ artist .set_animated (True )
1483+ self ._blit_artists .append (artist )
1484+ # the cached background may already contain this artist, so drop it and
1485+ # let the next update redraw (and re-cache) the figure without it
1486+ self ._blit_background = None
1487+ if self ._blit_cid is None :
1488+ # Grab a fresh background after every full redraw, whatever caused it
1489+ # (update_plot, draw_idle, a resize, a DPI change, ...).
1490+ self ._blit_cid = self .canvas .mpl_connect ("draw_event" , self ._on_draw )
1491+
1492+ def remove_blit_artist (self , artist ):
1493+ """Stop drawing an artist separately, putting it back in the background.
1494+
1495+ Parameters
1496+ ----------
1497+ artist : instance of matplotlib.artist.Artist
1498+ The artist to stop drawing separately. Artists that were never added
1499+ are ignored.
1500+ """
1501+ if artist not in self ._blit_artists :
1502+ return
1503+ self ._blit_artists .remove (artist )
1504+ artist .set_animated (False )
1505+ self .update_plot () # redraw so the artist becomes part of the background
1506+
1507+ def update_blit_artists (self ):
1508+ """Redraw only the artists added with :meth:`add_blit_artist`.
1509+
1510+ This is the fast path taken while the time line moves; any other change
1511+ to the figure needs :meth:`update_plot` instead.
1512+ """
1513+ if self ._blit_background is None or not self ._blit_artists :
1514+ self .update_plot () # nothing cached yet (or nothing to draw fast)
1515+ return
1516+ self .canvas .restore_region (self ._blit_background )
1517+ self ._draw_blit_artists ()
1518+ self .canvas .blit (self .fig .bbox )
1519+
1520+ def _draw_blit_artists (self ):
1521+ for artist in self ._blit_artists :
1522+ self .fig .draw_artist (artist )
1523+
1524+ def _on_draw (self , event = None ):
1525+ """Cache the background after a full redraw (draw_event callback)."""
1526+ self ._blit_background = self .canvas .copy_from_bbox (self .fig .bbox )
1527+ if not self .canvas .is_saving ():
1528+ # When saving, Matplotlib draws animated artists itself; drawing them
1529+ # again here would just double up their antialiasing.
1530+ self ._draw_blit_artists ()
1531+
14511532 def update_plot (self ):
14521533 """Update the plot."""
14531534 with warnings .catch_warnings (record = True ):
@@ -1503,6 +1584,11 @@ def close(self):
15031584 def clear (self ):
15041585 """Clear internal variables."""
15051586 self .close ()
1587+ if self ._blit_cid is not None :
1588+ self .canvas .mpl_disconnect (self ._blit_cid )
1589+ self ._blit_cid = None
1590+ self ._blit_artists .clear () # the artists go away with the figure below
1591+ self ._blit_background = None
15061592 self .axes .clear ()
15071593 self .fig .clear ()
15081594 self .canvas = None
0 commit comments