@@ -113,12 +113,21 @@ class AxesWidget(Widget):
113113 The parent figure canvas for the widget.
114114 active : bool
115115 If False, the widget does not respond to events.
116+ useblit : bool
117+ Whether usage of blitting is desired. The actual usage of
118+ blitting also depends on the canvas supporting it.
119+
120+ Once set, this is read-only, as some widgets currently still
121+ depend on the state statically. They still query _useblit.
122+
123+ .. versionadded:: 3.11
116124 """
117125
118- def __init__ (self , ax ):
126+ def __init__ (self , ax , useblit = False ):
119127 self .ax = ax
120128 self ._cids = []
121129 self ._blit_background_id = None
130+ self ._useblit = useblit
122131
123132 def __del__ (self ):
124133 if self ._blit_background_id is not None :
@@ -151,6 +160,34 @@ def _set_cursor(self, cursor):
151160 """Update the canvas cursor."""
152161 self .ax .get_figure (root = True ).canvas .set_cursor (cursor )
153162
163+ def _may_use_blit (self ):
164+ """
165+ Return whether blitting could potentially be used.
166+
167+ This is defined by the *useblit* parameter upon initialization
168+ and currently cannot be changed afterwards. Widgets can
169+ set up differently depending on this value. In particular they
170+ can create Artists with ``animated=self._may_use_blit()``.
171+ This makes sure these Artists properly work with blitting if
172+ that is applied, but is also safe to use if blitting is not
173+ applied.
174+
175+ Note: We define this separately from _useblit for semantic
176+ clarity. Eventually, we want to migrate widget code away from
177+ directly accessing _useblit.
178+ """
179+ return self ._useblit
180+
181+ def _should_use_blit (self ):
182+ """
183+ Return whether blitting should be used.
184+
185+ All blitting-related code must be guarded by this because
186+ not all canvases support blit and canvases may be swapped
187+ out during the lifetime of the widget.
188+ """
189+ return self ._useblit and self .canvas .supports_blit
190+
154191 def _save_blit_background (self , background ):
155192 """
156193 Save a blit background.
@@ -239,7 +276,7 @@ def __init__(self, ax, label, image=None,
239276
240277 .. versionadded:: 3.7
241278 """
242- super ().__init__ (ax )
279+ super ().__init__ (ax , useblit = useblit )
243280
244281 if image is not None :
245282 ax .imshow (image )
@@ -248,8 +285,6 @@ def __init__(self, ax, label, image=None,
248285 horizontalalignment = 'center' ,
249286 transform = ax .transAxes )
250287
251- self ._useblit = useblit
252-
253288 self ._observers = cbook .CallbackRegistry (signals = ["clicked" ])
254289
255290 self .connect_event ('button_press_event' , self ._click )
@@ -285,7 +320,7 @@ def _motion(self, event):
285320 if not colors .same_color (c , self .ax .get_facecolor ()):
286321 self .ax .set_facecolor (c )
287322 if self .drawon :
288- if self ._useblit and self . canvas . supports_blit :
323+ if self ._should_use_blit () :
289324 self .ax .draw_artist (self .ax )
290325 self .canvas .blit (self .ax .bbox )
291326 else :
@@ -1096,7 +1131,7 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
10961131
10971132 .. versionadded:: 3.7
10981133 """
1099- super ().__init__ (ax )
1134+ super ().__init__ (ax , useblit = useblit )
11001135
11011136 _api .check_isinstance ((dict , None ), label_props = label_props ,
11021137 frame_props = frame_props , check_props = check_props )
@@ -1108,8 +1143,6 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
11081143 if actives is None :
11091144 actives = [False ] * len (labels )
11101145
1111- self ._useblit = useblit
1112-
11131146 ys = np .linspace (1 , 0 , len (labels )+ 2 )[1 :- 1 ]
11141147
11151148 label_props = _expand_text_props (label_props )
@@ -1158,7 +1191,7 @@ def _clear(self, event):
11581191 """Internal event handler to clear the buttons."""
11591192 if self .ignore (event ) or self .canvas .is_saving ():
11601193 return
1161- if self ._useblit and self . canvas . supports_blit :
1194+ if self ._should_use_blit () :
11621195 self ._save_blit_background (self .canvas .copy_from_bbox (self .ax .bbox ))
11631196 self .ax .draw_artist (self ._checks )
11641197
@@ -1264,7 +1297,7 @@ def set_active(self, index, state=None):
12641297 self ._checks .set_facecolor (facecolors )
12651298
12661299 if self .drawon :
1267- if self ._useblit and self . canvas . supports_blit :
1300+ if self ._should_use_blit () :
12681301 background = self ._load_blit_background ()
12691302 if background is not None :
12701303 self .canvas .restore_region (background )
@@ -1678,7 +1711,7 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
16781711
16791712 .. versionadded:: 3.7
16801713 """
1681- super ().__init__ (ax )
1714+ super ().__init__ (ax , useblit = useblit )
16821715
16831716 _api .check_isinstance ((dict , None ), label_props = label_props ,
16841717 radio_props = radio_props )
@@ -1705,8 +1738,6 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
17051738
17061739 ys = np .linspace (1 , 0 , len (labels ) + 2 )[1 :- 1 ]
17071740
1708- self ._useblit = useblit
1709-
17101741 label_props = _expand_text_props (label_props )
17111742 self .labels = [
17121743 ax .text (0.25 , y , label , transform = ax .transAxes ,
@@ -1751,7 +1782,7 @@ def _clear(self, event):
17511782 """Internal event handler to clear the buttons."""
17521783 if self .ignore (event ) or self .canvas .is_saving ():
17531784 return
1754- if self ._useblit and self . canvas . supports_blit :
1785+ if self ._should_use_blit () :
17551786 self ._save_blit_background (self .canvas .copy_from_bbox (self .ax .bbox ))
17561787 self .ax .draw_artist (self ._buttons )
17571788
@@ -1845,7 +1876,7 @@ def set_active(self, index):
18451876 self ._buttons .set_facecolor (button_facecolors )
18461877
18471878 if self .drawon :
1848- if self ._useblit and self . canvas . supports_blit :
1879+ if self ._should_use_blit () :
18491880 background = self ._load_blit_background ()
18501881 if background is not None :
18511882 self .canvas .restore_region (background )
@@ -1989,7 +2020,7 @@ class Cursor(AxesWidget):
19892020 """
19902021 def __init__ (self , ax , * , horizOn = True , vertOn = True , useblit = False ,
19912022 ** lineprops ):
1992- super ().__init__ (ax )
2023+ super ().__init__ (ax , useblit = useblit )
19932024
19942025 self .connect_event ('motion_notify_event' , self .onmove )
19952026 self .connect_event ('draw_event' , self .clear )
@@ -2010,7 +2041,7 @@ def clear(self, event):
20102041 """Internal event handler to clear the cursor."""
20112042 if self .ignore (event ) or self .canvas .is_saving ():
20122043 return
2013- if self .useblit :
2044+ if self ._should_use_blit () :
20142045 self ._save_blit_background (self .canvas .copy_from_bbox (self .ax .bbox ))
20152046
20162047 @_call_with_reparented_event
@@ -2035,7 +2066,7 @@ def onmove(self, event):
20352066 if not (self .visible and (self .vertOn or self .horizOn )):
20362067 return
20372068 # Redraw.
2038- if self .useblit :
2069+ if self ._should_use_blit () :
20392070 background = self ._load_blit_background ()
20402071 if background is not None :
20412072 self .canvas .restore_region (background )
@@ -2205,14 +2236,13 @@ class _SelectorWidget(AxesWidget):
22052236
22062237 def __init__ (self , ax , onselect = None , useblit = False , button = None ,
22072238 state_modifier_keys = None , use_data_coordinates = False ):
2208- super ().__init__ (ax )
2239+ super ().__init__ (ax , useblit = useblit )
22092240
22102241 self ._visible = True
22112242 if onselect is None :
22122243 self .onselect = lambda * args : None
22132244 else :
22142245 self .onselect = onselect
2215- self ._useblit = useblit
22162246 self .connect_default_events ()
22172247
22182248 self ._state_modifier_keys = dict (move = ' ' , clear = 'escape' ,
@@ -2239,7 +2269,7 @@ def __init__(self, ax, onselect=None, useblit=False, button=None,
22392269 @property
22402270 def useblit (self ):
22412271 """Return whether blitting is used (requested and supported by canvas)."""
2242- return self ._useblit and self . canvas . supports_blit
2272+ return self ._should_use_blit ()
22432273
22442274 def set_active (self , active ):
22452275 super ().set_active (active )
@@ -2262,7 +2292,7 @@ def update_background(self, event):
22622292 """Force an update of the background."""
22632293 # If you add a call to `ignore` here, you'll want to check edge case:
22642294 # `release` can call a draw event even when `ignore` is True.
2265- if not self .useblit :
2295+ if not self ._should_use_blit () :
22662296 return
22672297 if self .canvas .is_saving ():
22682298 return # saving does not use blitting
@@ -2323,11 +2353,11 @@ def ignore(self, event):
23232353 event .button != self ._eventpress .button )
23242354
23252355 def update (self ):
2326- """Draw using blit() or draw_idle(), depending on ``self.useblit`` ."""
2356+ """Draw using blit() or draw_idle(), depending on blitting support ."""
23272357 if (not self .ax .get_visible () or
23282358 self .ax .get_figure (root = True )._get_renderer () is None ):
23292359 return
2330- if self .useblit :
2360+ if self ._should_use_blit () :
23312361 background = self ._load_blit_background ()
23322362 if background is not None :
23332363 self .canvas .restore_region (background )
@@ -2500,7 +2530,7 @@ def set_props(self, **props):
25002530 artist = self ._selection_artist
25012531 props = cbook .normalize_kwargs (props , artist )
25022532 artist .set (** props )
2503- if self .useblit :
2533+ if self ._should_use_blit () :
25042534 self .update ()
25052535
25062536 def set_handle_props (self , ** handle_props ):
@@ -2516,7 +2546,7 @@ def set_handle_props(self, **handle_props):
25162546 handle_props = cbook .normalize_kwargs (handle_props , artist )
25172547 for handle in self ._handles_artists :
25182548 handle .set (** handle_props )
2519- if self .useblit :
2549+ if self ._should_use_blit () :
25202550 self .update ()
25212551 self ._handle_props .update (handle_props )
25222552
@@ -2675,7 +2705,7 @@ def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False,
26752705 # This relies on the current behavior that the request for
26762706 # useblit is fixed during initialization and cannot be changed
26772707 # afterwards.
2678- props ['animated' ] = self ._useblit
2708+ props ['animated' ] = self ._may_use_blit ()
26792709
26802710 self .direction = direction
26812711 self ._extents_on_press = None
@@ -2741,7 +2771,7 @@ def _setup_edge_handles(self, props):
27412771 self ._edge_handles = ToolLineHandles (self .ax , positions ,
27422772 direction = self .direction ,
27432773 line_props = props ,
2744- useblit = self ._useblit )
2774+ useblit = self ._may_use_blit () )
27452775
27462776 @property
27472777 def _handles_artists (self ):
@@ -3315,7 +3345,7 @@ def __init__(self, ax, onselect=None, *, minspanx=0,
33153345 if props is None :
33163346 props = dict (facecolor = 'red' , edgecolor = 'black' ,
33173347 alpha = 0.2 , fill = True )
3318- props = {** props , 'animated' : self ._useblit }
3348+ props = {** props , 'animated' : self ._may_use_blit () }
33193349 self ._visible = props .pop ('visible' , self ._visible )
33203350 to_draw = self ._init_shape (** props )
33213351 self .ax .add_patch (to_draw )
@@ -3340,18 +3370,18 @@ def __init__(self, ax, onselect=None, *, minspanx=0,
33403370 xc , yc = self .corners
33413371 self ._corner_handles = ToolHandles (self .ax , xc , yc ,
33423372 marker_props = self ._handle_props ,
3343- useblit = self ._useblit )
3373+ useblit = self ._may_use_blit () )
33443374
33453375 self ._edge_order = ['W' , 'S' , 'E' , 'N' ]
33463376 xe , ye = self .edge_centers
33473377 self ._edge_handles = ToolHandles (self .ax , xe , ye , marker = 's' ,
33483378 marker_props = self ._handle_props ,
3349- useblit = self ._useblit )
3379+ useblit = self ._may_use_blit () )
33503380
33513381 xc , yc = self .center
33523382 self ._center_handle = ToolHandles (self .ax , [xc ], [yc ], marker = 's' ,
33533383 marker_props = self ._handle_props ,
3354- useblit = self ._useblit )
3384+ useblit = self ._may_use_blit () )
33553385
33563386 self ._active_handle = None
33573387
@@ -3856,9 +3886,7 @@ def __init__(self, ax, onselect=None, *, useblit=True, props=None, button=None):
38563886 self .verts = None
38573887 props = {
38583888 ** (props if props is not None else {}),
3859- # Note that self.useblit may be != useblit, if the canvas doesn't
3860- # support blitting.
3861- 'animated' : self ._useblit , 'visible' : False ,
3889+ 'animated' : self ._may_use_blit (), 'visible' : False ,
38623890 }
38633891 line = Line2D ([], [], ** props )
38643892 self .ax .add_line (line )
@@ -3983,7 +4011,7 @@ def __init__(self, ax, onselect=None, *, useblit=False,
39834011
39844012 if props is None :
39854013 props = dict (color = 'k' , linestyle = '-' , linewidth = 2 , alpha = 0.5 )
3986- props = {** props , 'animated' : self ._useblit }
4014+ props = {** props , 'animated' : self ._may_use_blit () }
39874015 self ._selection_artist = line = Line2D ([], [], ** props )
39884016 self .ax .add_line (line )
39894017
@@ -3992,7 +4020,7 @@ def __init__(self, ax, onselect=None, *, useblit=False,
39924020 markerfacecolor = props .get ('color' , 'k' ))
39934021 self ._handle_props = handle_props
39944022 self ._polygon_handles = ToolHandles (self .ax , [], [],
3995- useblit = self ._useblit ,
4023+ useblit = self ._may_use_blit () ,
39964024 marker_props = self ._handle_props )
39974025
39984026 self ._active_handle_idx = - 1
@@ -4012,7 +4040,7 @@ def _get_bbox(self):
40124040
40134041 def _add_box (self ):
40144042 self ._box = RectangleSelector (self .ax ,
4015- useblit = self ._useblit ,
4043+ useblit = self ._may_use_blit () ,
40164044 grab_range = self .grab_range ,
40174045 handle_props = self ._box_handle_props ,
40184046 props = self ._box_props ,
0 commit comments