@@ -118,6 +118,11 @@ class AxesWidget(Widget):
118118 def __init__ (self , ax ):
119119 self .ax = ax
120120 self ._cids = []
121+ self ._blit_background_id = None
122+
123+ def __del__ (self ):
124+ if self ._blit_background_id is not None :
125+ self .canvas ._release_blit_background_id (self ._blit_background_id )
121126
122127 canvas = property (
123128 lambda self : getattr (self .ax .get_figure (root = True ), 'canvas' , None )
@@ -146,6 +151,26 @@ def _set_cursor(self, cursor):
146151 """Update the canvas cursor."""
147152 self .ax .get_figure (root = True ).canvas .set_cursor (cursor )
148153
154+ def _save_blit_background (self , background ):
155+ """
156+ Save a blit background.
157+
158+ The background is stored on the canvas in a uniquely identifiable way.
159+ It should be read back via `._load_blit_background`. Be prepared that
160+ some events may invalidate the background, in which case
161+ `._load_blit_background` will return None.
162+
163+ This currently allows at most one background per widget, which is
164+ good enough for all existing widgets.
165+ """
166+ if self ._blit_background_id is None :
167+ self ._blit_background_id = self .canvas ._get_blit_background_id ()
168+ self .canvas ._blit_backgrounds [self ._blit_background_id ] = background
169+
170+ def _load_blit_background (self ):
171+ """Load a blit background; may be None at any time."""
172+ return self .canvas ._blit_backgrounds .get (self ._blit_background_id )
173+
149174
150175def _call_with_reparented_event (func ):
151176 """
@@ -223,7 +248,7 @@ def __init__(self, ax, label, image=None,
223248 horizontalalignment = 'center' ,
224249 transform = ax .transAxes )
225250
226- self ._useblit = useblit and self . canvas . supports_blit
251+ self ._useblit = useblit
227252
228253 self ._observers = cbook .CallbackRegistry (signals = ["clicked" ])
229254
@@ -260,7 +285,7 @@ def _motion(self, event):
260285 if not colors .same_color (c , self .ax .get_facecolor ()):
261286 self .ax .set_facecolor (c )
262287 if self .drawon :
263- if self ._useblit :
288+ if self ._useblit and self . canvas . supports_blit :
264289 self .ax .draw_artist (self .ax )
265290 self .canvas .blit (self .ax .bbox )
266291 else :
@@ -1083,8 +1108,7 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
10831108 if actives is None :
10841109 actives = [False ] * len (labels )
10851110
1086- self ._useblit = useblit and self .canvas .supports_blit
1087- self ._background = None
1111+ self ._useblit = useblit and self .canvas .supports_blit # TODO: make dynamic
10881112
10891113 ys = np .linspace (1 , 0 , len (labels )+ 2 )[1 :- 1 ]
10901114
@@ -1131,7 +1155,7 @@ def _clear(self, event):
11311155 """Internal event handler to clear the buttons."""
11321156 if self .ignore (event ) or self .canvas .is_saving ():
11331157 return
1134- self ._background = self .canvas .copy_from_bbox (self .ax .bbox )
1158+ self ._save_blit_background ( self .canvas .copy_from_bbox (self .ax .bbox ) )
11351159 self .ax .draw_artist (self ._checks )
11361160
11371161 @_call_with_reparented_event
@@ -1237,8 +1261,9 @@ def set_active(self, index, state=None):
12371261
12381262 if self .drawon :
12391263 if self ._useblit :
1240- if self ._background is not None :
1241- self .canvas .restore_region (self ._background )
1264+ background = self ._load_blit_background ()
1265+ if background is not None :
1266+ self .canvas .restore_region (background )
12421267 self .ax .draw_artist (self ._checks )
12431268 self .canvas .blit (self .ax .bbox )
12441269 else :
@@ -1676,8 +1701,7 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
16761701
16771702 ys = np .linspace (1 , 0 , len (labels ) + 2 )[1 :- 1 ]
16781703
1679- self ._useblit = useblit and self .canvas .supports_blit
1680- self ._background = None
1704+ self ._useblit = useblit and self .canvas .supports_blit # TODO: make dynamic
16811705
16821706 label_props = _expand_text_props (label_props )
16831707 self .labels = [
@@ -1719,7 +1743,7 @@ def _clear(self, event):
17191743 """Internal event handler to clear the buttons."""
17201744 if self .ignore (event ) or self .canvas .is_saving ():
17211745 return
1722- self ._background = self .canvas .copy_from_bbox (self .ax .bbox )
1746+ self ._save_blit_background ( self .canvas .copy_from_bbox (self .ax .bbox ) )
17231747 self .ax .draw_artist (self ._buttons )
17241748
17251749 @_call_with_reparented_event
@@ -1813,8 +1837,9 @@ def set_active(self, index):
18131837
18141838 if self .drawon :
18151839 if self ._useblit :
1816- if self ._background is not None :
1817- self .canvas .restore_region (self ._background )
1840+ background = self ._load_blit_background ()
1841+ if background is not None :
1842+ self .canvas .restore_region (background )
18181843 self .ax .draw_artist (self ._buttons )
18191844 self .canvas .blit (self .ax .bbox )
18201845 else :
@@ -1963,22 +1988,21 @@ def __init__(self, ax, *, horizOn=True, vertOn=True, useblit=False,
19631988 self .visible = True
19641989 self .horizOn = horizOn
19651990 self .vertOn = vertOn
1966- self .useblit = useblit and self .canvas .supports_blit
1991+ self .useblit = useblit and self .canvas .supports_blit # TODO: make dynamic
19671992
19681993 if self .useblit :
19691994 lineprops ['animated' ] = True
19701995 self .lineh = ax .axhline (ax .get_ybound ()[0 ], visible = False , ** lineprops )
19711996 self .linev = ax .axvline (ax .get_xbound ()[0 ], visible = False , ** lineprops )
19721997
1973- self .background = None
19741998 self .needclear = False
19751999
19762000 def clear (self , event ):
19772001 """Internal event handler to clear the cursor."""
19782002 if self .ignore (event ) or self .canvas .is_saving ():
19792003 return
19802004 if self .useblit :
1981- self .background = self .canvas .copy_from_bbox (self .ax .bbox )
2005+ self ._save_blit_background ( self .canvas .copy_from_bbox (self .ax .bbox ) )
19822006
19832007 @_call_with_reparented_event
19842008 def onmove (self , event ):
@@ -2003,8 +2027,9 @@ def onmove(self, event):
20032027 return
20042028 # Redraw.
20052029 if self .useblit :
2006- if self .background is not None :
2007- self .canvas .restore_region (self .background )
2030+ background = self ._load_blit_background ()
2031+ if background is not None :
2032+ self .canvas .restore_region (background )
20082033 self .ax .draw_artist (self .linev )
20092034 self .ax .draw_artist (self .lineh )
20102035 self .canvas .blit (self .ax .bbox )
@@ -2093,6 +2118,7 @@ def __init__(self, *args, useblit=True, horizOn=False, vertOn=True,
20932118 self .useblit = (
20942119 useblit
20952120 and all (canvas .supports_blit for canvas in self ._canvas_infos ))
2121+ # TODO: make dynamic
20962122
20972123 if self .useblit :
20982124 lineprops ['animated' ] = True
@@ -2177,7 +2203,7 @@ def __init__(self, ax, onselect=None, useblit=False, button=None,
21772203 self .onselect = lambda * args : None
21782204 else :
21792205 self .onselect = onselect
2180- self .useblit = useblit and self . canvas . supports_blit
2206+ self ._useblit = useblit
21812207 self .connect_default_events ()
21822208
21832209 self ._state_modifier_keys = dict (move = ' ' , clear = 'escape' ,
@@ -2186,8 +2212,6 @@ def __init__(self, ax, onselect=None, useblit=False, button=None,
21862212 self ._state_modifier_keys .update (state_modifier_keys or {})
21872213 self ._use_data_coordinates = use_data_coordinates
21882214
2189- self .background = None
2190-
21912215 if isinstance (button , Integral ):
21922216 self .validButtons = [button ]
21932217 else :
@@ -2203,6 +2227,11 @@ def __init__(self, ax, onselect=None, useblit=False, button=None,
22032227 self ._prev_event = None
22042228 self ._state = set ()
22052229
2230+ @property
2231+ def useblit (self ):
2232+ """Return whether blitting is used (requested and supported by canvas)."""
2233+ return self ._useblit and self .canvas .supports_blit
2234+
22062235 def set_active (self , active ):
22072236 super ().set_active (active )
22082237 if active :
@@ -2243,7 +2272,7 @@ def update_background(self, event):
22432272 for artist in artists :
22442273 stack .enter_context (artist ._cm_set (visible = False ))
22452274 self .canvas .draw ()
2246- self .background = self .canvas .copy_from_bbox (self .ax .bbox )
2275+ self ._save_blit_background ( self .canvas .copy_from_bbox (self .ax .bbox ) )
22472276 if needs_redraw :
22482277 for artist in artists :
22492278 self .ax .draw_artist (artist )
@@ -2290,8 +2319,9 @@ def update(self):
22902319 self .ax .get_figure (root = True )._get_renderer () is None ):
22912320 return
22922321 if self .useblit :
2293- if self .background is not None :
2294- self .canvas .restore_region (self .background )
2322+ background = self ._load_blit_background ()
2323+ if background is not None :
2324+ self .canvas .restore_region (background )
22952325 else :
22962326 self .update_background (None )
22972327 # We need to draw all artists, which are not included in the
@@ -2629,7 +2659,14 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False,
26292659 if props is None :
26302660 props = dict (facecolor = 'red' , alpha = 0.5 )
26312661
2632- props ['animated' ] = self .useblit
2662+ # Note: We set this based on the user setting during ínitialization,
2663+ # not on the actual capability of blitting. But the value is
2664+ # irrelevant if the backend does not support blitting, so that
2665+ # we don't have to dynamically update this on the backend.
2666+ # This relies on the current behavior that the request for
2667+ # useblit is fixed during initialization and cannot be changed
2668+ # afterwards.
2669+ props ['animated' ] = self ._useblit
26332670
26342671 self .direction = direction
26352672 self ._extents_on_press = None
@@ -2695,7 +2732,7 @@ def _setup_edge_handles(self, props):
26952732 self ._edge_handles = ToolLineHandles (self .ax , positions ,
26962733 direction = self .direction ,
26972734 line_props = props ,
2698- useblit = self .useblit )
2735+ useblit = self ._useblit )
26992736
27002737 @property
27012738 def _handles_artists (self ):
@@ -3269,7 +3306,7 @@ def __init__(self, ax, onselect=None, *, minspanx=0,
32693306 if props is None :
32703307 props = dict (facecolor = 'red' , edgecolor = 'black' ,
32713308 alpha = 0.2 , fill = True )
3272- props = {** props , 'animated' : self .useblit }
3309+ props = {** props , 'animated' : self ._useblit }
32733310 self ._visible = props .pop ('visible' , self ._visible )
32743311 to_draw = self ._init_shape (** props )
32753312 self .ax .add_patch (to_draw )
@@ -3294,18 +3331,18 @@ def __init__(self, ax, onselect=None, *, minspanx=0,
32943331 xc , yc = self .corners
32953332 self ._corner_handles = ToolHandles (self .ax , xc , yc ,
32963333 marker_props = self ._handle_props ,
3297- useblit = self .useblit )
3334+ useblit = self ._useblit )
32983335
32993336 self ._edge_order = ['W' , 'S' , 'E' , 'N' ]
33003337 xe , ye = self .edge_centers
33013338 self ._edge_handles = ToolHandles (self .ax , xe , ye , marker = 's' ,
33023339 marker_props = self ._handle_props ,
3303- useblit = self .useblit )
3340+ useblit = self ._useblit )
33043341
33053342 xc , yc = self .center
33063343 self ._center_handle = ToolHandles (self .ax , [xc ], [yc ], marker = 's' ,
33073344 marker_props = self ._handle_props ,
3308- useblit = self .useblit )
3345+ useblit = self ._useblit )
33093346
33103347 self ._active_handle = None
33113348
@@ -3812,7 +3849,7 @@ def __init__(self, ax, onselect=None, *, useblit=True, props=None, button=None):
38123849 ** (props if props is not None else {}),
38133850 # Note that self.useblit may be != useblit, if the canvas doesn't
38143851 # support blitting.
3815- 'animated' : self .useblit , 'visible' : False ,
3852+ 'animated' : self ._useblit , 'visible' : False ,
38163853 }
38173854 line = Line2D ([], [], ** props )
38183855 self .ax .add_line (line )
@@ -3937,7 +3974,7 @@ def __init__(self, ax, onselect=None, *, useblit=False,
39373974
39383975 if props is None :
39393976 props = dict (color = 'k' , linestyle = '-' , linewidth = 2 , alpha = 0.5 )
3940- props = {** props , 'animated' : self .useblit }
3977+ props = {** props , 'animated' : self ._useblit }
39413978 self ._selection_artist = line = Line2D ([], [], ** props )
39423979 self .ax .add_line (line )
39433980
@@ -3946,7 +3983,7 @@ def __init__(self, ax, onselect=None, *, useblit=False,
39463983 markerfacecolor = props .get ('color' , 'k' ))
39473984 self ._handle_props = handle_props
39483985 self ._polygon_handles = ToolHandles (self .ax , [], [],
3949- useblit = self .useblit ,
3986+ useblit = self ._useblit ,
39503987 marker_props = self ._handle_props )
39513988
39523989 self ._active_handle_idx = - 1
@@ -3966,7 +4003,7 @@ def _get_bbox(self):
39664003
39674004 def _add_box (self ):
39684005 self ._box = RectangleSelector (self .ax ,
3969- useblit = self .useblit ,
4006+ useblit = self ._useblit ,
39704007 grab_range = self .grab_range ,
39714008 handle_props = self ._box_handle_props ,
39724009 props = self ._box_props ,
@@ -4248,7 +4285,7 @@ class Lasso(AxesWidget):
42484285 def __init__ (self , ax , xy , callback , * , useblit = True , props = None ):
42494286 super ().__init__ (ax )
42504287
4251- self .useblit = useblit and self .canvas .supports_blit
4288+ self .useblit = useblit and self .canvas .supports_blit # TODO: Make dynamic
42524289 if self .useblit :
42534290 self .background = self .canvas .copy_from_bbox (self .ax .bbox )
42544291
0 commit comments