@@ -111,12 +111,21 @@ class AxesWidget(Widget):
111111 The parent figure canvas for the widget.
112112 active : bool
113113 If False, the widget does not respond to events.
114+ useblit : bool
115+ Whether usage of blitting is desired. The actual usage of
116+ blitting also depends on the canvas supporting it.
117+
118+ Once set, this is read-only, as some widgets currently still
119+ depend on the state statically. They still query _useblit.
120+
121+ .. versionadded:: 3.11
114122 """
115123
116- def __init__ (self , ax ):
124+ def __init__ (self , ax , useblit = False ):
117125 self .ax = ax
118126 self ._cids = []
119127 self ._blit_background_id = None
128+ self ._useblit = useblit
120129
121130 def __del__ (self ):
122131 if self ._blit_background_id is not None :
@@ -149,6 +158,34 @@ def _set_cursor(self, cursor):
149158 """Update the canvas cursor."""
150159 self .ax .get_figure (root = True ).canvas .set_cursor (cursor )
151160
161+ def _may_use_blit (self ):
162+ """
163+ Return whether blitting could potentially be used.
164+
165+ This is defined by the *useblit* parameter upon initialization
166+ and currently cannot be changed afterwards. Widgets can
167+ set up differently depending on this value. In particular they
168+ can create Artists with ``animated=self._may_use_blit()``.
169+ This makes sure these Artists properly work with blitting if
170+ that is applied, but is also safe to use if blitting is not
171+ applied.
172+
173+ Note: We define this separately from _useblit for semantic
174+ clarity. Eventually, we want to migrate widget code away from
175+ directly accessing _useblit.
176+ """
177+ return self ._useblit
178+
179+ def _should_use_blit (self ):
180+ """
181+ Return whether blitting should be used.
182+
183+ All blitting-related code must be guarded by this because
184+ not all canvases support blit and canvases may be swapped
185+ out during the lifetime of the widget.
186+ """
187+ return self ._useblit and self .canvas .supports_blit
188+
152189 def _save_blit_background (self , background ):
153190 """
154191 Save a blit background.
@@ -237,7 +274,7 @@ def __init__(self, ax, label, image=None,
237274
238275 .. versionadded:: 3.7
239276 """
240- super ().__init__ (ax )
277+ super ().__init__ (ax , useblit = useblit )
241278
242279 if image is not None :
243280 ax .imshow (image )
@@ -246,8 +283,6 @@ def __init__(self, ax, label, image=None,
246283 horizontalalignment = 'center' ,
247284 transform = ax .transAxes )
248285
249- self ._useblit = useblit
250-
251286 self ._observers = cbook .CallbackRegistry (signals = ["clicked" ])
252287
253288 self .connect_event ('button_press_event' , self ._click )
@@ -283,7 +318,7 @@ def _motion(self, event):
283318 if not colors .same_color (c , self .ax .get_facecolor ()):
284319 self .ax .set_facecolor (c )
285320 if self .drawon :
286- if self ._useblit and self . canvas . supports_blit :
321+ if self ._should_use_blit () :
287322 self .ax .draw_artist (self .ax )
288323 self .canvas .blit (self .ax .bbox )
289324 else :
@@ -1050,14 +1085,12 @@ class _Buttons(AxesWidget):
10501085 """
10511086
10521087 def __init__ (self , ax , labels , * , useblit = True , label_props = None , ** kwargs ):
1053- super ().__init__ (ax )
1088+ super ().__init__ (ax , useblit = useblit )
10541089
10551090 ax .set_xticks ([])
10561091 ax .set_yticks ([])
10571092 ax .set_navigate (False )
10581093
1059- self ._useblit = useblit
1060-
10611094 self ._buttons_ys = np .linspace (1 , 0 , len (labels )+ 2 )[1 :- 1 ]
10621095
10631096 label_props = _expand_text_props (label_props )
@@ -1072,7 +1105,7 @@ def __init__(self, ax, labels, *, useblit=True, label_props=None, **kwargs):
10721105 self ._init_props (text_size , ** kwargs )
10731106
10741107 self .connect_event ('button_press_event' , self ._clicked )
1075- if self ._useblit :
1108+ if self ._may_use_blit () :
10761109 self .connect_event ('draw_event' , self ._clear )
10771110
10781111 self ._observers = cbook .CallbackRegistry (signals = ["clicked" ])
@@ -1084,7 +1117,7 @@ def _clear(self, event):
10841117 """Internal event handler to clear the buttons."""
10851118 if self .ignore (event ) or self .canvas .is_saving ():
10861119 return
1087- if self ._useblit and self . canvas . supports_blit :
1120+ if self ._should_use_blit () :
10881121 self ._save_blit_background (self .canvas .copy_from_bbox (self .ax .bbox ))
10891122 self .ax .draw_artist (self ._buttons )
10901123
@@ -1320,7 +1353,7 @@ def set_active(self, index, state=None):
13201353 self ._buttons .set_facecolor (facecolors )
13211354
13221355 if self .drawon :
1323- if self ._useblit and self . canvas . supports_blit :
1356+ if self ._should_use_blit () :
13241357 background = self ._load_blit_background ()
13251358 if background is not None :
13261359 self .canvas .restore_region (background )
@@ -1822,7 +1855,7 @@ def set_active(self, index):
18221855 self ._buttons .set_facecolor (button_facecolors )
18231856
18241857 if self .drawon :
1825- if self ._useblit and self . canvas . supports_blit :
1858+ if self ._should_use_blit () :
18261859 background = self ._load_blit_background ()
18271860 if background is not None :
18281861 self .canvas .restore_region (background )
@@ -1941,7 +1974,7 @@ class Cursor(AxesWidget):
19411974 """
19421975 def __init__ (self , ax , * , horizOn = True , vertOn = True , useblit = False ,
19431976 ** lineprops ):
1944- super ().__init__ (ax )
1977+ super ().__init__ (ax , useblit = useblit )
19451978
19461979 self .connect_event ('motion_notify_event' , self .onmove )
19471980 self .connect_event ('draw_event' , self .clear )
@@ -1962,7 +1995,7 @@ def clear(self, event):
19621995 """Internal event handler to clear the cursor."""
19631996 if self .ignore (event ) or self .canvas .is_saving ():
19641997 return
1965- if self .useblit :
1998+ if self ._should_use_blit () :
19661999 self ._save_blit_background (self .canvas .copy_from_bbox (self .ax .bbox ))
19672000
19682001 @_call_with_reparented_event
@@ -1987,7 +2020,7 @@ def onmove(self, event):
19872020 if not (self .visible and (self .vertOn or self .horizOn )):
19882021 return
19892022 # Redraw.
1990- if self .useblit :
2023+ if self ._should_use_blit () :
19912024 background = self ._load_blit_background ()
19922025 if background is not None :
19932026 self .canvas .restore_region (background )
@@ -2167,14 +2200,13 @@ class _SelectorWidget(AxesWidget):
21672200
21682201 def __init__ (self , ax , onselect = None , useblit = False , button = None ,
21692202 state_modifier_keys = None , use_data_coordinates = False ):
2170- super ().__init__ (ax )
2203+ super ().__init__ (ax , useblit = useblit )
21712204
21722205 self ._visible = True
21732206 if onselect is None :
21742207 self .onselect = lambda * args : None
21752208 else :
21762209 self .onselect = onselect
2177- self ._useblit = useblit
21782210 self .connect_default_events ()
21792211
21802212 self ._state_modifier_keys = dict (move = ' ' , clear = 'escape' ,
@@ -2201,7 +2233,7 @@ def __init__(self, ax, onselect=None, useblit=False, button=None,
22012233 @property
22022234 def useblit (self ):
22032235 """Return whether blitting is used (requested and supported by canvas)."""
2204- return self ._useblit and self . canvas . supports_blit
2236+ return self ._should_use_blit ()
22052237
22062238 def set_active (self , active ):
22072239 super ().set_active (active )
@@ -2224,7 +2256,7 @@ def update_background(self, event):
22242256 """Force an update of the background."""
22252257 # If you add a call to `ignore` here, you'll want to check edge case:
22262258 # `release` can call a draw event even when `ignore` is True.
2227- if not self .useblit :
2259+ if not self ._should_use_blit () :
22282260 return
22292261 if self .canvas .is_saving ():
22302262 return # saving does not use blitting
@@ -2285,11 +2317,11 @@ def ignore(self, event):
22852317 event .button != self ._eventpress .button )
22862318
22872319 def update (self ):
2288- """Draw using blit() or draw_idle(), depending on ``self.useblit`` ."""
2320+ """Draw using blit() or draw_idle(), depending on blitting support ."""
22892321 if (not self .ax .get_visible () or
22902322 self .ax .get_figure (root = True )._get_renderer () is None ):
22912323 return
2292- if self .useblit :
2324+ if self ._should_use_blit () :
22932325 background = self ._load_blit_background ()
22942326 if background is not None :
22952327 self .canvas .restore_region (background )
@@ -2462,7 +2494,7 @@ def set_props(self, **props):
24622494 artist = self ._selection_artist
24632495 props = cbook .normalize_kwargs (props , artist )
24642496 artist .set (** props )
2465- if self .useblit :
2497+ if self ._should_use_blit () :
24662498 self .update ()
24672499
24682500 def set_handle_props (self , ** handle_props ):
@@ -2478,7 +2510,7 @@ def set_handle_props(self, **handle_props):
24782510 handle_props = cbook .normalize_kwargs (handle_props , artist )
24792511 for handle in self ._handles_artists :
24802512 handle .set (** handle_props )
2481- if self .useblit :
2513+ if self ._should_use_blit () :
24822514 self .update ()
24832515 self ._handle_props .update (handle_props )
24842516
@@ -2641,7 +2673,7 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False,
26412673 # This relies on the current behavior that the request for
26422674 # useblit is fixed during initialization and cannot be changed
26432675 # afterwards.
2644- props ['animated' ] = self ._useblit
2676+ props ['animated' ] = self ._may_use_blit ()
26452677
26462678 self .direction = direction
26472679 self ._extents_on_press = None
@@ -2707,7 +2739,7 @@ def _setup_edge_handles(self, props):
27072739 self ._edge_handles = ToolLineHandles (self .ax , positions ,
27082740 direction = self .direction ,
27092741 line_props = props ,
2710- useblit = self ._useblit )
2742+ useblit = self ._may_use_blit () )
27112743
27122744 @property
27132745 def _handles_artists (self ):
@@ -3281,7 +3313,7 @@ def __init__(self, ax, onselect=None, *, minspanx=0,
32813313 if props is None :
32823314 props = dict (facecolor = 'red' , edgecolor = 'black' ,
32833315 alpha = 0.2 , fill = True )
3284- props = {** props , 'animated' : self ._useblit }
3316+ props = {** props , 'animated' : self ._may_use_blit () }
32853317 self ._visible = props .pop ('visible' , self ._visible )
32863318 to_draw = self ._init_shape (** props )
32873319 self .ax .add_patch (to_draw )
@@ -3306,18 +3338,18 @@ def __init__(self, ax, onselect=None, *, minspanx=0,
33063338 xc , yc = self .corners
33073339 self ._corner_handles = ToolHandles (self .ax , xc , yc ,
33083340 marker_props = self ._handle_props ,
3309- useblit = self ._useblit )
3341+ useblit = self ._may_use_blit () )
33103342
33113343 self ._edge_order = ['W' , 'S' , 'E' , 'N' ]
33123344 xe , ye = self .edge_centers
33133345 self ._edge_handles = ToolHandles (self .ax , xe , ye , marker = 's' ,
33143346 marker_props = self ._handle_props ,
3315- useblit = self ._useblit )
3347+ useblit = self ._may_use_blit () )
33163348
33173349 xc , yc = self .center
33183350 self ._center_handle = ToolHandles (self .ax , [xc ], [yc ], marker = 's' ,
33193351 marker_props = self ._handle_props ,
3320- useblit = self ._useblit )
3352+ useblit = self ._may_use_blit () )
33213353
33223354 self ._active_handle = None
33233355
@@ -3822,9 +3854,7 @@ def __init__(self, ax, onselect=None, *, useblit=True, props=None, button=None):
38223854 self .verts = None
38233855 props = {
38243856 ** (props if props is not None else {}),
3825- # Note that self.useblit may be != useblit, if the canvas doesn't
3826- # support blitting.
3827- 'animated' : self ._useblit , 'visible' : False ,
3857+ 'animated' : self ._may_use_blit (), 'visible' : False ,
38283858 }
38293859 line = Line2D ([], [], ** props )
38303860 self .ax .add_line (line )
@@ -3949,7 +3979,7 @@ def __init__(self, ax, onselect=None, *, useblit=False,
39493979
39503980 if props is None :
39513981 props = dict (color = 'k' , linestyle = '-' , linewidth = 2 , alpha = 0.5 )
3952- props = {** props , 'animated' : self ._useblit }
3982+ props = {** props , 'animated' : self ._may_use_blit () }
39533983 self ._selection_artist = line = Line2D ([], [], ** props )
39543984 self .ax .add_line (line )
39553985
@@ -3958,7 +3988,7 @@ def __init__(self, ax, onselect=None, *, useblit=False,
39583988 markerfacecolor = props .get ('color' , 'k' ))
39593989 self ._handle_props = handle_props
39603990 self ._polygon_handles = ToolHandles (self .ax , [], [],
3961- useblit = self ._useblit ,
3991+ useblit = self ._may_use_blit () ,
39623992 marker_props = self ._handle_props )
39633993
39643994 self ._active_handle_idx = - 1
@@ -3978,7 +4008,7 @@ def _get_bbox(self):
39784008
39794009 def _add_box (self ):
39804010 self ._box = RectangleSelector (self .ax ,
3981- useblit = self ._useblit ,
4011+ useblit = self ._may_use_blit () ,
39824012 grab_range = self .grab_range ,
39834013 handle_props = self ._box_handle_props ,
39844014 props = self ._box_props ,
0 commit comments