Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/artist.py: 33%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

668 statements  

1from collections import namedtuple 

2import contextlib 

3from functools import cache, wraps 

4import inspect 

5from inspect import Signature, Parameter 

6import logging 

7from numbers import Number, Real 

8import re 

9import warnings 

10 

11import numpy as np 

12 

13import matplotlib as mpl 

14from . import _api, cbook 

15from .colors import BoundaryNorm 

16from .cm import ScalarMappable 

17from .path import Path 

18from .transforms import (BboxBase, Bbox, IdentityTransform, Transform, TransformedBbox, 

19 TransformedPatchPath, TransformedPath) 

20 

21_log = logging.getLogger(__name__) 

22 

23 

24def _prevent_rasterization(draw): 

25 # We assume that by default artists are not allowed to rasterize (unless 

26 # its draw method is explicitly decorated). If it is being drawn after a 

27 # rasterized artist and it has reached a raster_depth of 0, we stop 

28 # rasterization so that it does not affect the behavior of normal artist 

29 # (e.g., change in dpi). 

30 

31 @wraps(draw) 

32 def draw_wrapper(artist, renderer, *args, **kwargs): 

33 if renderer._raster_depth == 0 and renderer._rasterizing: 

34 # Only stop when we are not in a rasterized parent 

35 # and something has been rasterized since last stop. 

36 renderer.stop_rasterizing() 

37 renderer._rasterizing = False 

38 

39 return draw(artist, renderer, *args, **kwargs) 

40 

41 draw_wrapper._supports_rasterization = False 

42 return draw_wrapper 

43 

44 

45def allow_rasterization(draw): 

46 """ 

47 Decorator for Artist.draw method. Provides routines 

48 that run before and after the draw call. The before and after functions 

49 are useful for changing artist-dependent renderer attributes or making 

50 other setup function calls, such as starting and flushing a mixed-mode 

51 renderer. 

52 """ 

53 

54 @wraps(draw) 

55 def draw_wrapper(artist, renderer): 

56 try: 

57 if artist.get_rasterized(): 

58 if renderer._raster_depth == 0 and not renderer._rasterizing: 

59 renderer.start_rasterizing() 

60 renderer._rasterizing = True 

61 renderer._raster_depth += 1 

62 else: 

63 if renderer._raster_depth == 0 and renderer._rasterizing: 

64 # Only stop when we are not in a rasterized parent 

65 # and something has be rasterized since last stop 

66 renderer.stop_rasterizing() 

67 renderer._rasterizing = False 

68 

69 if artist.get_agg_filter() is not None: 

70 renderer.start_filter() 

71 

72 return draw(artist, renderer) 

73 finally: 

74 if artist.get_agg_filter() is not None: 

75 renderer.stop_filter(artist.get_agg_filter()) 

76 if artist.get_rasterized(): 

77 renderer._raster_depth -= 1 

78 if (renderer._rasterizing and artist.figure and 

79 artist.figure.suppressComposite): 

80 # restart rasterizing to prevent merging 

81 renderer.stop_rasterizing() 

82 renderer.start_rasterizing() 

83 

84 draw_wrapper._supports_rasterization = True 

85 return draw_wrapper 

86 

87 

88def _finalize_rasterization(draw): 

89 """ 

90 Decorator for Artist.draw method. Needed on the outermost artist, i.e. 

91 Figure, to finish up if the render is still in rasterized mode. 

92 """ 

93 @wraps(draw) 

94 def draw_wrapper(artist, renderer, *args, **kwargs): 

95 result = draw(artist, renderer, *args, **kwargs) 

96 if renderer._rasterizing: 

97 renderer.stop_rasterizing() 

98 renderer._rasterizing = False 

99 return result 

100 return draw_wrapper 

101 

102 

103def _stale_axes_callback(self, val): 

104 if self.axes: 

105 self.axes.stale = val 

106 

107 

108_XYPair = namedtuple("_XYPair", "x y") 

109 

110 

111class _Unset: 

112 def __repr__(self): 

113 return "<UNSET>" 

114_UNSET = _Unset() 

115 

116 

117class Artist: 

118 """ 

119 Abstract base class for objects that render into a FigureCanvas. 

120 

121 Typically, all visible elements in a figure are subclasses of Artist. 

122 """ 

123 

124 zorder = 0 

125 

126 def __init_subclass__(cls): 

127 

128 # Decorate draw() method so that all artists are able to stop 

129 # rastrization when necessary. If the artist's draw method is already 

130 # decorated (has a `_supports_rasterization` attribute), it won't be 

131 # decorated. 

132 

133 if not hasattr(cls.draw, "_supports_rasterization"): 

134 cls.draw = _prevent_rasterization(cls.draw) 

135 

136 # Inject custom set() methods into the subclass with signature and 

137 # docstring based on the subclasses' properties. 

138 

139 if not hasattr(cls.set, '_autogenerated_signature'): 

140 # Don't overwrite cls.set if the subclass or one of its parents 

141 # has defined a set method set itself. 

142 # If there was no explicit definition, cls.set is inherited from 

143 # the hierarchy of auto-generated set methods, which hold the 

144 # flag _autogenerated_signature. 

145 return 

146 

147 cls.set = lambda self, **kwargs: Artist.set(self, **kwargs) 

148 cls.set.__name__ = "set" 

149 cls.set.__qualname__ = f"{cls.__qualname__}.set" 

150 cls._update_set_signature_and_docstring() 

151 

152 _PROPERTIES_EXCLUDED_FROM_SET = [ 

153 'navigate_mode', # not a user-facing function 

154 'figure', # changing the figure is such a profound operation 

155 # that we don't want this in set() 

156 '3d_properties', # cannot be used as a keyword due to leading digit 

157 ] 

158 

159 @classmethod 

160 def _update_set_signature_and_docstring(cls): 

161 """ 

162 Update the signature of the set function to list all properties 

163 as keyword arguments. 

164 

165 Property aliases are not listed in the signature for brevity, but 

166 are still accepted as keyword arguments. 

167 """ 

168 cls.set.__signature__ = Signature( 

169 [Parameter("self", Parameter.POSITIONAL_OR_KEYWORD), 

170 *[Parameter(prop, Parameter.KEYWORD_ONLY, default=_UNSET) 

171 for prop in ArtistInspector(cls).get_setters() 

172 if prop not in Artist._PROPERTIES_EXCLUDED_FROM_SET]]) 

173 cls.set._autogenerated_signature = True 

174 

175 cls.set.__doc__ = ( 

176 "Set multiple properties at once.\n\n" 

177 "Supported properties are\n\n" 

178 + kwdoc(cls)) 

179 

180 def __init__(self): 

181 self._stale = True 

182 self.stale_callback = None 

183 self._axes = None 

184 self.figure = None 

185 

186 self._transform = None 

187 self._transformSet = False 

188 self._visible = True 

189 self._animated = False 

190 self._alpha = None 

191 self.clipbox = None 

192 self._clippath = None 

193 self._clipon = True 

194 self._label = '' 

195 self._picker = None 

196 self._rasterized = False 

197 self._agg_filter = None 

198 # Normally, artist classes need to be queried for mouseover info if and 

199 # only if they override get_cursor_data. 

200 self._mouseover = type(self).get_cursor_data != Artist.get_cursor_data 

201 self._callbacks = cbook.CallbackRegistry(signals=["pchanged"]) 

202 try: 

203 self.axes = None 

204 except AttributeError: 

205 # Handle self.axes as a read-only property, as in Figure. 

206 pass 

207 self._remove_method = None 

208 self._url = None 

209 self._gid = None 

210 self._snap = None 

211 self._sketch = mpl.rcParams['path.sketch'] 

212 self._path_effects = mpl.rcParams['path.effects'] 

213 self._sticky_edges = _XYPair([], []) 

214 self._in_layout = True 

215 

216 def __getstate__(self): 

217 d = self.__dict__.copy() 

218 d['stale_callback'] = None 

219 return d 

220 

221 def remove(self): 

222 """ 

223 Remove the artist from the figure if possible. 

224 

225 The effect will not be visible until the figure is redrawn, e.g., 

226 with `.FigureCanvasBase.draw_idle`. Call `~.axes.Axes.relim` to 

227 update the Axes limits if desired. 

228 

229 Note: `~.axes.Axes.relim` will not see collections even if the 

230 collection was added to the Axes with *autolim* = True. 

231 

232 Note: there is no support for removing the artist's legend entry. 

233 """ 

234 

235 # There is no method to set the callback. Instead, the parent should 

236 # set the _remove_method attribute directly. This would be a 

237 # protected attribute if Python supported that sort of thing. The 

238 # callback has one parameter, which is the child to be removed. 

239 if self._remove_method is not None: 

240 self._remove_method(self) 

241 # clear stale callback 

242 self.stale_callback = None 

243 _ax_flag = False 

244 if hasattr(self, 'axes') and self.axes: 

245 # remove from the mouse hit list 

246 self.axes._mouseover_set.discard(self) 

247 self.axes.stale = True 

248 self.axes = None # decouple the artist from the Axes 

249 _ax_flag = True 

250 

251 if self.figure: 

252 if not _ax_flag: 

253 self.figure.stale = True 

254 self.figure = None 

255 

256 else: 

257 raise NotImplementedError('cannot remove artist') 

258 # TODO: the fix for the collections relim problem is to move the 

259 # limits calculation into the artist itself, including the property of 

260 # whether or not the artist should affect the limits. Then there will 

261 # be no distinction between axes.add_line, axes.add_patch, etc. 

262 # TODO: add legend support 

263 

264 def have_units(self): 

265 """Return whether units are set on any axis.""" 

266 ax = self.axes 

267 return ax and any(axis.have_units() for axis in ax._axis_map.values()) 

268 

269 def convert_xunits(self, x): 

270 """ 

271 Convert *x* using the unit type of the xaxis. 

272 

273 If the artist is not contained in an Axes or if the xaxis does not 

274 have units, *x* itself is returned. 

275 """ 

276 ax = getattr(self, 'axes', None) 

277 if ax is None or ax.xaxis is None: 

278 return x 

279 return ax.xaxis.convert_units(x) 

280 

281 def convert_yunits(self, y): 

282 """ 

283 Convert *y* using the unit type of the yaxis. 

284 

285 If the artist is not contained in an Axes or if the yaxis does not 

286 have units, *y* itself is returned. 

287 """ 

288 ax = getattr(self, 'axes', None) 

289 if ax is None or ax.yaxis is None: 

290 return y 

291 return ax.yaxis.convert_units(y) 

292 

293 @property 

294 def axes(self): 

295 """The `~.axes.Axes` instance the artist resides in, or *None*.""" 

296 return self._axes 

297 

298 @axes.setter 

299 def axes(self, new_axes): 

300 if (new_axes is not None and self._axes is not None 

301 and new_axes != self._axes): 

302 raise ValueError("Can not reset the Axes. You are probably trying to reuse " 

303 "an artist in more than one Axes which is not supported") 

304 self._axes = new_axes 

305 if new_axes is not None and new_axes is not self: 

306 self.stale_callback = _stale_axes_callback 

307 

308 @property 

309 def stale(self): 

310 """ 

311 Whether the artist is 'stale' and needs to be re-drawn for the output 

312 to match the internal state of the artist. 

313 """ 

314 return self._stale 

315 

316 @stale.setter 

317 def stale(self, val): 

318 self._stale = val 

319 

320 # if the artist is animated it does not take normal part in the 

321 # draw stack and is not expected to be drawn as part of the normal 

322 # draw loop (when not saving) so do not propagate this change 

323 if self._animated: 

324 return 

325 

326 if val and self.stale_callback is not None: 

327 self.stale_callback(self, val) 

328 

329 def get_window_extent(self, renderer=None): 

330 """ 

331 Get the artist's bounding box in display space. 

332 

333 The bounding box' width and height are nonnegative. 

334 

335 Subclasses should override for inclusion in the bounding box 

336 "tight" calculation. Default is to return an empty bounding 

337 box at 0, 0. 

338 

339 Be careful when using this function, the results will not update 

340 if the artist window extent of the artist changes. The extent 

341 can change due to any changes in the transform stack, such as 

342 changing the Axes limits, the figure size, or the canvas used 

343 (as is done when saving a figure). This can lead to unexpected 

344 behavior where interactive figures will look fine on the screen, 

345 but will save incorrectly. 

346 """ 

347 return Bbox([[0, 0], [0, 0]]) 

348 

349 def get_tightbbox(self, renderer=None): 

350 """ 

351 Like `.Artist.get_window_extent`, but includes any clipping. 

352 

353 Parameters 

354 ---------- 

355 renderer : `~matplotlib.backend_bases.RendererBase` subclass, optional 

356 renderer that will be used to draw the figures (i.e. 

357 ``fig.canvas.get_renderer()``) 

358 

359 Returns 

360 ------- 

361 `.Bbox` or None 

362 The enclosing bounding box (in figure pixel coordinates). 

363 Returns None if clipping results in no intersection. 

364 """ 

365 bbox = self.get_window_extent(renderer) 

366 if self.get_clip_on(): 

367 clip_box = self.get_clip_box() 

368 if clip_box is not None: 

369 bbox = Bbox.intersection(bbox, clip_box) 

370 clip_path = self.get_clip_path() 

371 if clip_path is not None and bbox is not None: 

372 clip_path = clip_path.get_fully_transformed_path() 

373 bbox = Bbox.intersection(bbox, clip_path.get_extents()) 

374 return bbox 

375 

376 def add_callback(self, func): 

377 """ 

378 Add a callback function that will be called whenever one of the 

379 `.Artist`'s properties changes. 

380 

381 Parameters 

382 ---------- 

383 func : callable 

384 The callback function. It must have the signature:: 

385 

386 def func(artist: Artist) -> Any 

387 

388 where *artist* is the calling `.Artist`. Return values may exist 

389 but are ignored. 

390 

391 Returns 

392 ------- 

393 int 

394 The observer id associated with the callback. This id can be 

395 used for removing the callback with `.remove_callback` later. 

396 

397 See Also 

398 -------- 

399 remove_callback 

400 """ 

401 # Wrapping func in a lambda ensures it can be connected multiple times 

402 # and never gets weakref-gc'ed. 

403 return self._callbacks.connect("pchanged", lambda: func(self)) 

404 

405 def remove_callback(self, oid): 

406 """ 

407 Remove a callback based on its observer id. 

408 

409 See Also 

410 -------- 

411 add_callback 

412 """ 

413 self._callbacks.disconnect(oid) 

414 

415 def pchanged(self): 

416 """ 

417 Call all of the registered callbacks. 

418 

419 This function is triggered internally when a property is changed. 

420 

421 See Also 

422 -------- 

423 add_callback 

424 remove_callback 

425 """ 

426 self._callbacks.process("pchanged") 

427 

428 def is_transform_set(self): 

429 """ 

430 Return whether the Artist has an explicitly set transform. 

431 

432 This is *True* after `.set_transform` has been called. 

433 """ 

434 return self._transformSet 

435 

436 def set_transform(self, t): 

437 """ 

438 Set the artist transform. 

439 

440 Parameters 

441 ---------- 

442 t : `~matplotlib.transforms.Transform` 

443 """ 

444 self._transform = t 

445 self._transformSet = True 

446 self.pchanged() 

447 self.stale = True 

448 

449 def get_transform(self): 

450 """Return the `.Transform` instance used by this artist.""" 

451 if self._transform is None: 

452 self._transform = IdentityTransform() 

453 elif (not isinstance(self._transform, Transform) 

454 and hasattr(self._transform, '_as_mpl_transform')): 

455 self._transform = self._transform._as_mpl_transform(self.axes) 

456 return self._transform 

457 

458 def get_children(self): 

459 r"""Return a list of the child `.Artist`\s of this `.Artist`.""" 

460 return [] 

461 

462 def _different_canvas(self, event): 

463 """ 

464 Check whether an *event* occurred on a canvas other that this artist's canvas. 

465 

466 If this method returns True, the event definitely occurred on a different 

467 canvas; if it returns False, either it occurred on the same canvas, or we may 

468 not have enough information to know. 

469 

470 Subclasses should start their definition of `contains` as follows:: 

471 

472 if self._different_canvas(mouseevent): 

473 return False, {} 

474 # subclass-specific implementation follows 

475 """ 

476 return (getattr(event, "canvas", None) is not None and self.figure is not None 

477 and event.canvas is not self.figure.canvas) 

478 

479 def contains(self, mouseevent): 

480 """ 

481 Test whether the artist contains the mouse event. 

482 

483 Parameters 

484 ---------- 

485 mouseevent : `~matplotlib.backend_bases.MouseEvent` 

486 

487 Returns 

488 ------- 

489 contains : bool 

490 Whether any values are within the radius. 

491 details : dict 

492 An artist-specific dictionary of details of the event context, 

493 such as which points are contained in the pick radius. See the 

494 individual Artist subclasses for details. 

495 """ 

496 _log.warning("%r needs 'contains' method", self.__class__.__name__) 

497 return False, {} 

498 

499 def pickable(self): 

500 """ 

501 Return whether the artist is pickable. 

502 

503 See Also 

504 -------- 

505 .Artist.set_picker, .Artist.get_picker, .Artist.pick 

506 """ 

507 return self.figure is not None and self._picker is not None 

508 

509 def pick(self, mouseevent): 

510 """ 

511 Process a pick event. 

512 

513 Each child artist will fire a pick event if *mouseevent* is over 

514 the artist and the artist has picker set. 

515 

516 See Also 

517 -------- 

518 .Artist.set_picker, .Artist.get_picker, .Artist.pickable 

519 """ 

520 from .backend_bases import PickEvent # Circular import. 

521 # Pick self 

522 if self.pickable(): 

523 picker = self.get_picker() 

524 if callable(picker): 

525 inside, prop = picker(self, mouseevent) 

526 else: 

527 inside, prop = self.contains(mouseevent) 

528 if inside: 

529 PickEvent("pick_event", self.figure.canvas, 

530 mouseevent, self, **prop)._process() 

531 

532 # Pick children 

533 for a in self.get_children(): 

534 # make sure the event happened in the same Axes 

535 ax = getattr(a, 'axes', None) 

536 if (isinstance(a, mpl.figure.SubFigure) 

537 or mouseevent.inaxes is None or ax is None 

538 or mouseevent.inaxes == ax): 

539 # we need to check if mouseevent.inaxes is None 

540 # because some objects associated with an Axes (e.g., a 

541 # tick label) can be outside the bounding box of the 

542 # Axes and inaxes will be None 

543 # also check that ax is None so that it traverse objects 

544 # which do not have an axes property but children might 

545 a.pick(mouseevent) 

546 

547 def set_picker(self, picker): 

548 """ 

549 Define the picking behavior of the artist. 

550 

551 Parameters 

552 ---------- 

553 picker : None or bool or float or callable 

554 This can be one of the following: 

555 

556 - *None*: Picking is disabled for this artist (default). 

557 

558 - A boolean: If *True* then picking will be enabled and the 

559 artist will fire a pick event if the mouse event is over 

560 the artist. 

561 

562 - A float: If picker is a number it is interpreted as an 

563 epsilon tolerance in points and the artist will fire 

564 off an event if its data is within epsilon of the mouse 

565 event. For some artists like lines and patch collections, 

566 the artist may provide additional data to the pick event 

567 that is generated, e.g., the indices of the data within 

568 epsilon of the pick event 

569 

570 - A function: If picker is callable, it is a user supplied 

571 function which determines whether the artist is hit by the 

572 mouse event:: 

573 

574 hit, props = picker(artist, mouseevent) 

575 

576 to determine the hit test. if the mouse event is over the 

577 artist, return *hit=True* and props is a dictionary of 

578 properties you want added to the PickEvent attributes. 

579 """ 

580 self._picker = picker 

581 

582 def get_picker(self): 

583 """ 

584 Return the picking behavior of the artist. 

585 

586 The possible values are described in `.Artist.set_picker`. 

587 

588 See Also 

589 -------- 

590 .Artist.set_picker, .Artist.pickable, .Artist.pick 

591 """ 

592 return self._picker 

593 

594 def get_url(self): 

595 """Return the url.""" 

596 return self._url 

597 

598 def set_url(self, url): 

599 """ 

600 Set the url for the artist. 

601 

602 Parameters 

603 ---------- 

604 url : str 

605 """ 

606 self._url = url 

607 

608 def get_gid(self): 

609 """Return the group id.""" 

610 return self._gid 

611 

612 def set_gid(self, gid): 

613 """ 

614 Set the (group) id for the artist. 

615 

616 Parameters 

617 ---------- 

618 gid : str 

619 """ 

620 self._gid = gid 

621 

622 def get_snap(self): 

623 """ 

624 Return the snap setting. 

625 

626 See `.set_snap` for details. 

627 """ 

628 if mpl.rcParams['path.snap']: 

629 return self._snap 

630 else: 

631 return False 

632 

633 def set_snap(self, snap): 

634 """ 

635 Set the snapping behavior. 

636 

637 Snapping aligns positions with the pixel grid, which results in 

638 clearer images. For example, if a black line of 1px width was 

639 defined at a position in between two pixels, the resulting image 

640 would contain the interpolated value of that line in the pixel grid, 

641 which would be a grey value on both adjacent pixel positions. In 

642 contrast, snapping will move the line to the nearest integer pixel 

643 value, so that the resulting image will really contain a 1px wide 

644 black line. 

645 

646 Snapping is currently only supported by the Agg and MacOSX backends. 

647 

648 Parameters 

649 ---------- 

650 snap : bool or None 

651 Possible values: 

652 

653 - *True*: Snap vertices to the nearest pixel center. 

654 - *False*: Do not modify vertex positions. 

655 - *None*: (auto) If the path contains only rectilinear line 

656 segments, round to the nearest pixel center. 

657 """ 

658 self._snap = snap 

659 self.stale = True 

660 

661 def get_sketch_params(self): 

662 """ 

663 Return the sketch parameters for the artist. 

664 

665 Returns 

666 ------- 

667 tuple or None 

668 

669 A 3-tuple with the following elements: 

670 

671 - *scale*: The amplitude of the wiggle perpendicular to the 

672 source line. 

673 - *length*: The length of the wiggle along the line. 

674 - *randomness*: The scale factor by which the length is 

675 shrunken or expanded. 

676 

677 Returns *None* if no sketch parameters were set. 

678 """ 

679 return self._sketch 

680 

681 def set_sketch_params(self, scale=None, length=None, randomness=None): 

682 """ 

683 Set the sketch parameters. 

684 

685 Parameters 

686 ---------- 

687 scale : float, optional 

688 The amplitude of the wiggle perpendicular to the source 

689 line, in pixels. If scale is `None`, or not provided, no 

690 sketch filter will be provided. 

691 length : float, optional 

692 The length of the wiggle along the line, in pixels 

693 (default 128.0) 

694 randomness : float, optional 

695 The scale factor by which the length is shrunken or 

696 expanded (default 16.0) 

697 

698 The PGF backend uses this argument as an RNG seed and not as 

699 described above. Using the same seed yields the same random shape. 

700 

701 .. ACCEPTS: (scale: float, length: float, randomness: float) 

702 """ 

703 if scale is None: 

704 self._sketch = None 

705 else: 

706 self._sketch = (scale, length or 128.0, randomness or 16.0) 

707 self.stale = True 

708 

709 def set_path_effects(self, path_effects): 

710 """ 

711 Set the path effects. 

712 

713 Parameters 

714 ---------- 

715 path_effects : list of `.AbstractPathEffect` 

716 """ 

717 self._path_effects = path_effects 

718 self.stale = True 

719 

720 def get_path_effects(self): 

721 return self._path_effects 

722 

723 def get_figure(self): 

724 """Return the `.Figure` instance the artist belongs to.""" 

725 return self.figure 

726 

727 def set_figure(self, fig): 

728 """ 

729 Set the `.Figure` instance the artist belongs to. 

730 

731 Parameters 

732 ---------- 

733 fig : `~matplotlib.figure.Figure` 

734 """ 

735 # if this is a no-op just return 

736 if self.figure is fig: 

737 return 

738 # if we currently have a figure (the case of both `self.figure` 

739 # and *fig* being none is taken care of above) we then user is 

740 # trying to change the figure an artist is associated with which 

741 # is not allowed for the same reason as adding the same instance 

742 # to more than one Axes 

743 if self.figure is not None: 

744 raise RuntimeError("Can not put single artist in " 

745 "more than one figure") 

746 self.figure = fig 

747 if self.figure and self.figure is not self: 

748 self.pchanged() 

749 self.stale = True 

750 

751 def set_clip_box(self, clipbox): 

752 """ 

753 Set the artist's clip `.Bbox`. 

754 

755 Parameters 

756 ---------- 

757 clipbox : `~matplotlib.transforms.BboxBase` or None 

758 Will typically be created from a `.TransformedBbox`. For instance, 

759 ``TransformedBbox(Bbox([[0, 0], [1, 1]]), ax.transAxes)`` is the default 

760 clipping for an artist added to an Axes. 

761 

762 """ 

763 _api.check_isinstance((BboxBase, None), clipbox=clipbox) 

764 if clipbox != self.clipbox: 

765 self.clipbox = clipbox 

766 self.pchanged() 

767 self.stale = True 

768 

769 def set_clip_path(self, path, transform=None): 

770 """ 

771 Set the artist's clip path. 

772 

773 Parameters 

774 ---------- 

775 path : `~matplotlib.patches.Patch` or `.Path` or `.TransformedPath` or None 

776 The clip path. If given a `.Path`, *transform* must be provided as 

777 well. If *None*, a previously set clip path is removed. 

778 transform : `~matplotlib.transforms.Transform`, optional 

779 Only used if *path* is a `.Path`, in which case the given `.Path` 

780 is converted to a `.TransformedPath` using *transform*. 

781 

782 Notes 

783 ----- 

784 For efficiency, if *path* is a `.Rectangle` this method will set the 

785 clipping box to the corresponding rectangle and set the clipping path 

786 to ``None``. 

787 

788 For technical reasons (support of `~.Artist.set`), a tuple 

789 (*path*, *transform*) is also accepted as a single positional 

790 parameter. 

791 

792 .. ACCEPTS: Patch or (Path, Transform) or None 

793 """ 

794 from matplotlib.patches import Patch, Rectangle 

795 

796 success = False 

797 if transform is None: 

798 if isinstance(path, Rectangle): 

799 self.clipbox = TransformedBbox(Bbox.unit(), 

800 path.get_transform()) 

801 self._clippath = None 

802 success = True 

803 elif isinstance(path, Patch): 

804 self._clippath = TransformedPatchPath(path) 

805 success = True 

806 elif isinstance(path, tuple): 

807 path, transform = path 

808 

809 if path is None: 

810 self._clippath = None 

811 success = True 

812 elif isinstance(path, Path): 

813 self._clippath = TransformedPath(path, transform) 

814 success = True 

815 elif isinstance(path, TransformedPatchPath): 

816 self._clippath = path 

817 success = True 

818 elif isinstance(path, TransformedPath): 

819 self._clippath = path 

820 success = True 

821 

822 if not success: 

823 raise TypeError( 

824 "Invalid arguments to set_clip_path, of type " 

825 f"{type(path).__name__} and {type(transform).__name__}") 

826 # This may result in the callbacks being hit twice, but guarantees they 

827 # will be hit at least once. 

828 self.pchanged() 

829 self.stale = True 

830 

831 def get_alpha(self): 

832 """ 

833 Return the alpha value used for blending - not supported on all 

834 backends. 

835 """ 

836 return self._alpha 

837 

838 def get_visible(self): 

839 """Return the visibility.""" 

840 return self._visible 

841 

842 def get_animated(self): 

843 """Return whether the artist is animated.""" 

844 return self._animated 

845 

846 def get_in_layout(self): 

847 """ 

848 Return boolean flag, ``True`` if artist is included in layout 

849 calculations. 

850 

851 E.g. :ref:`constrainedlayout_guide`, 

852 `.Figure.tight_layout()`, and 

853 ``fig.savefig(fname, bbox_inches='tight')``. 

854 """ 

855 return self._in_layout 

856 

857 def _fully_clipped_to_axes(self): 

858 """ 

859 Return a boolean flag, ``True`` if the artist is clipped to the Axes 

860 and can thus be skipped in layout calculations. Requires `get_clip_on` 

861 is True, one of `clip_box` or `clip_path` is set, ``clip_box.extents`` 

862 is equivalent to ``ax.bbox.extents`` (if set), and ``clip_path._patch`` 

863 is equivalent to ``ax.patch`` (if set). 

864 """ 

865 # Note that ``clip_path.get_fully_transformed_path().get_extents()`` 

866 # cannot be directly compared to ``axes.bbox.extents`` because the 

867 # extents may be undefined (i.e. equivalent to ``Bbox.null()``) 

868 # before the associated artist is drawn, and this method is meant 

869 # to determine whether ``axes.get_tightbbox()`` may bypass drawing 

870 clip_box = self.get_clip_box() 

871 clip_path = self.get_clip_path() 

872 return (self.axes is not None 

873 and self.get_clip_on() 

874 and (clip_box is not None or clip_path is not None) 

875 and (clip_box is None 

876 or np.all(clip_box.extents == self.axes.bbox.extents)) 

877 and (clip_path is None 

878 or isinstance(clip_path, TransformedPatchPath) 

879 and clip_path._patch is self.axes.patch)) 

880 

881 def get_clip_on(self): 

882 """Return whether the artist uses clipping.""" 

883 return self._clipon 

884 

885 def get_clip_box(self): 

886 """Return the clipbox.""" 

887 return self.clipbox 

888 

889 def get_clip_path(self): 

890 """Return the clip path.""" 

891 return self._clippath 

892 

893 def get_transformed_clip_path_and_affine(self): 

894 """ 

895 Return the clip path with the non-affine part of its 

896 transformation applied, and the remaining affine part of its 

897 transformation. 

898 """ 

899 if self._clippath is not None: 

900 return self._clippath.get_transformed_path_and_affine() 

901 return None, None 

902 

903 def set_clip_on(self, b): 

904 """ 

905 Set whether the artist uses clipping. 

906 

907 When False, artists will be visible outside the Axes which 

908 can lead to unexpected results. 

909 

910 Parameters 

911 ---------- 

912 b : bool 

913 """ 

914 self._clipon = b 

915 # This may result in the callbacks being hit twice, but ensures they 

916 # are hit at least once 

917 self.pchanged() 

918 self.stale = True 

919 

920 def _set_gc_clip(self, gc): 

921 """Set the clip properly for the gc.""" 

922 if self._clipon: 

923 if self.clipbox is not None: 

924 gc.set_clip_rectangle(self.clipbox) 

925 gc.set_clip_path(self._clippath) 

926 else: 

927 gc.set_clip_rectangle(None) 

928 gc.set_clip_path(None) 

929 

930 def get_rasterized(self): 

931 """Return whether the artist is to be rasterized.""" 

932 return self._rasterized 

933 

934 def set_rasterized(self, rasterized): 

935 """ 

936 Force rasterized (bitmap) drawing for vector graphics output. 

937 

938 Rasterized drawing is not supported by all artists. If you try to 

939 enable this on an artist that does not support it, the command has no 

940 effect and a warning will be issued. 

941 

942 This setting is ignored for pixel-based output. 

943 

944 See also :doc:`/gallery/misc/rasterization_demo`. 

945 

946 Parameters 

947 ---------- 

948 rasterized : bool 

949 """ 

950 supports_rasterization = getattr(self.draw, 

951 "_supports_rasterization", False) 

952 if rasterized and not supports_rasterization: 

953 _api.warn_external(f"Rasterization of '{self}' will be ignored") 

954 

955 self._rasterized = rasterized 

956 

957 def get_agg_filter(self): 

958 """Return filter function to be used for agg filter.""" 

959 return self._agg_filter 

960 

961 def set_agg_filter(self, filter_func): 

962 """ 

963 Set the agg filter. 

964 

965 Parameters 

966 ---------- 

967 filter_func : callable 

968 A filter function, which takes a (m, n, depth) float array 

969 and a dpi value, and returns a (m, n, depth) array and two 

970 offsets from the bottom left corner of the image 

971 

972 .. ACCEPTS: a filter function, which takes a (m, n, 3) float array 

973 and a dpi value, and returns a (m, n, 3) array and two offsets 

974 from the bottom left corner of the image 

975 """ 

976 self._agg_filter = filter_func 

977 self.stale = True 

978 

979 def draw(self, renderer): 

980 """ 

981 Draw the Artist (and its children) using the given renderer. 

982 

983 This has no effect if the artist is not visible (`.Artist.get_visible` 

984 returns False). 

985 

986 Parameters 

987 ---------- 

988 renderer : `~matplotlib.backend_bases.RendererBase` subclass. 

989 

990 Notes 

991 ----- 

992 This method is overridden in the Artist subclasses. 

993 """ 

994 if not self.get_visible(): 

995 return 

996 self.stale = False 

997 

998 def set_alpha(self, alpha): 

999 """ 

1000 Set the alpha value used for blending - not supported on all backends. 

1001 

1002 Parameters 

1003 ---------- 

1004 alpha : scalar or None 

1005 *alpha* must be within the 0-1 range, inclusive. 

1006 """ 

1007 if alpha is not None and not isinstance(alpha, Real): 

1008 raise TypeError( 

1009 f'alpha must be numeric or None, not {type(alpha)}') 

1010 if alpha is not None and not (0 <= alpha <= 1): 

1011 raise ValueError(f'alpha ({alpha}) is outside 0-1 range') 

1012 if alpha != self._alpha: 

1013 self._alpha = alpha 

1014 self.pchanged() 

1015 self.stale = True 

1016 

1017 def _set_alpha_for_array(self, alpha): 

1018 """ 

1019 Set the alpha value used for blending - not supported on all backends. 

1020 

1021 Parameters 

1022 ---------- 

1023 alpha : array-like or scalar or None 

1024 All values must be within the 0-1 range, inclusive. 

1025 Masked values and nans are not supported. 

1026 """ 

1027 if isinstance(alpha, str): 

1028 raise TypeError("alpha must be numeric or None, not a string") 

1029 if not np.iterable(alpha): 

1030 Artist.set_alpha(self, alpha) 

1031 return 

1032 alpha = np.asarray(alpha) 

1033 if not (0 <= alpha.min() and alpha.max() <= 1): 

1034 raise ValueError('alpha must be between 0 and 1, inclusive, ' 

1035 f'but min is {alpha.min()}, max is {alpha.max()}') 

1036 self._alpha = alpha 

1037 self.pchanged() 

1038 self.stale = True 

1039 

1040 def set_visible(self, b): 

1041 """ 

1042 Set the artist's visibility. 

1043 

1044 Parameters 

1045 ---------- 

1046 b : bool 

1047 """ 

1048 if b != self._visible: 

1049 self._visible = b 

1050 self.pchanged() 

1051 self.stale = True 

1052 

1053 def set_animated(self, b): 

1054 """ 

1055 Set whether the artist is intended to be used in an animation. 

1056 

1057 If True, the artist is excluded from regular drawing of the figure. 

1058 You have to call `.Figure.draw_artist` / `.Axes.draw_artist` 

1059 explicitly on the artist. This approach is used to speed up animations 

1060 using blitting. 

1061 

1062 See also `matplotlib.animation` and 

1063 :ref:`blitting`. 

1064 

1065 Parameters 

1066 ---------- 

1067 b : bool 

1068 """ 

1069 if self._animated != b: 

1070 self._animated = b 

1071 self.pchanged() 

1072 

1073 def set_in_layout(self, in_layout): 

1074 """ 

1075 Set if artist is to be included in layout calculations, 

1076 E.g. :ref:`constrainedlayout_guide`, 

1077 `.Figure.tight_layout()`, and 

1078 ``fig.savefig(fname, bbox_inches='tight')``. 

1079 

1080 Parameters 

1081 ---------- 

1082 in_layout : bool 

1083 """ 

1084 self._in_layout = in_layout 

1085 

1086 def get_label(self): 

1087 """Return the label used for this artist in the legend.""" 

1088 return self._label 

1089 

1090 def set_label(self, s): 

1091 """ 

1092 Set a label that will be displayed in the legend. 

1093 

1094 Parameters 

1095 ---------- 

1096 s : object 

1097 *s* will be converted to a string by calling `str`. 

1098 """ 

1099 label = str(s) if s is not None else None 

1100 if label != self._label: 

1101 self._label = label 

1102 self.pchanged() 

1103 self.stale = True 

1104 

1105 def get_zorder(self): 

1106 """Return the artist's zorder.""" 

1107 return self.zorder 

1108 

1109 def set_zorder(self, level): 

1110 """ 

1111 Set the zorder for the artist. Artists with lower zorder 

1112 values are drawn first. 

1113 

1114 Parameters 

1115 ---------- 

1116 level : float 

1117 """ 

1118 if level is None: 

1119 level = self.__class__.zorder 

1120 if level != self.zorder: 

1121 self.zorder = level 

1122 self.pchanged() 

1123 self.stale = True 

1124 

1125 @property 

1126 def sticky_edges(self): 

1127 """ 

1128 ``x`` and ``y`` sticky edge lists for autoscaling. 

1129 

1130 When performing autoscaling, if a data limit coincides with a value in 

1131 the corresponding sticky_edges list, then no margin will be added--the 

1132 view limit "sticks" to the edge. A typical use case is histograms, 

1133 where one usually expects no margin on the bottom edge (0) of the 

1134 histogram. 

1135 

1136 Moreover, margin expansion "bumps" against sticky edges and cannot 

1137 cross them. For example, if the upper data limit is 1.0, the upper 

1138 view limit computed by simple margin application is 1.2, but there is a 

1139 sticky edge at 1.1, then the actual upper view limit will be 1.1. 

1140 

1141 This attribute cannot be assigned to; however, the ``x`` and ``y`` 

1142 lists can be modified in place as needed. 

1143 

1144 Examples 

1145 -------- 

1146 >>> artist.sticky_edges.x[:] = (xmin, xmax) 

1147 >>> artist.sticky_edges.y[:] = (ymin, ymax) 

1148 

1149 """ 

1150 return self._sticky_edges 

1151 

1152 def update_from(self, other): 

1153 """Copy properties from *other* to *self*.""" 

1154 self._transform = other._transform 

1155 self._transformSet = other._transformSet 

1156 self._visible = other._visible 

1157 self._alpha = other._alpha 

1158 self.clipbox = other.clipbox 

1159 self._clipon = other._clipon 

1160 self._clippath = other._clippath 

1161 self._label = other._label 

1162 self._sketch = other._sketch 

1163 self._path_effects = other._path_effects 

1164 self.sticky_edges.x[:] = other.sticky_edges.x.copy() 

1165 self.sticky_edges.y[:] = other.sticky_edges.y.copy() 

1166 self.pchanged() 

1167 self.stale = True 

1168 

1169 def properties(self): 

1170 """Return a dictionary of all the properties of the artist.""" 

1171 return ArtistInspector(self).properties() 

1172 

1173 def _update_props(self, props, errfmt): 

1174 """ 

1175 Helper for `.Artist.set` and `.Artist.update`. 

1176 

1177 *errfmt* is used to generate error messages for invalid property 

1178 names; it gets formatted with ``type(self)`` and the property name. 

1179 """ 

1180 ret = [] 

1181 with cbook._setattr_cm(self, eventson=False): 

1182 for k, v in props.items(): 

1183 # Allow attributes we want to be able to update through 

1184 # art.update, art.set, setp. 

1185 if k == "axes": 

1186 ret.append(setattr(self, k, v)) 

1187 else: 

1188 func = getattr(self, f"set_{k}", None) 

1189 if not callable(func): 

1190 raise AttributeError( 

1191 errfmt.format(cls=type(self), prop_name=k)) 

1192 ret.append(func(v)) 

1193 if ret: 

1194 self.pchanged() 

1195 self.stale = True 

1196 return ret 

1197 

1198 def update(self, props): 

1199 """ 

1200 Update this artist's properties from the dict *props*. 

1201 

1202 Parameters 

1203 ---------- 

1204 props : dict 

1205 """ 

1206 return self._update_props( 

1207 props, "{cls.__name__!r} object has no property {prop_name!r}") 

1208 

1209 def _internal_update(self, kwargs): 

1210 """ 

1211 Update artist properties without prenormalizing them, but generating 

1212 errors as if calling `set`. 

1213 

1214 The lack of prenormalization is to maintain backcompatibility. 

1215 """ 

1216 return self._update_props( 

1217 kwargs, "{cls.__name__}.set() got an unexpected keyword argument " 

1218 "{prop_name!r}") 

1219 

1220 def set(self, **kwargs): 

1221 # docstring and signature are auto-generated via 

1222 # Artist._update_set_signature_and_docstring() at the end of the 

1223 # module. 

1224 return self._internal_update(cbook.normalize_kwargs(kwargs, self)) 

1225 

1226 @contextlib.contextmanager 

1227 def _cm_set(self, **kwargs): 

1228 """ 

1229 `.Artist.set` context-manager that restores original values at exit. 

1230 """ 

1231 orig_vals = {k: getattr(self, f"get_{k}")() for k in kwargs} 

1232 try: 

1233 self.set(**kwargs) 

1234 yield 

1235 finally: 

1236 self.set(**orig_vals) 

1237 

1238 def findobj(self, match=None, include_self=True): 

1239 """ 

1240 Find artist objects. 

1241 

1242 Recursively find all `.Artist` instances contained in the artist. 

1243 

1244 Parameters 

1245 ---------- 

1246 match 

1247 A filter criterion for the matches. This can be 

1248 

1249 - *None*: Return all objects contained in artist. 

1250 - A function with signature ``def match(artist: Artist) -> bool``. 

1251 The result will only contain artists for which the function 

1252 returns *True*. 

1253 - A class instance: e.g., `.Line2D`. The result will only contain 

1254 artists of this class or its subclasses (``isinstance`` check). 

1255 

1256 include_self : bool 

1257 Include *self* in the list to be checked for a match. 

1258 

1259 Returns 

1260 ------- 

1261 list of `.Artist` 

1262 

1263 """ 

1264 if match is None: # always return True 

1265 def matchfunc(x): 

1266 return True 

1267 elif isinstance(match, type) and issubclass(match, Artist): 

1268 def matchfunc(x): 

1269 return isinstance(x, match) 

1270 elif callable(match): 

1271 matchfunc = match 

1272 else: 

1273 raise ValueError('match must be None, a matplotlib.artist.Artist ' 

1274 'subclass, or a callable') 

1275 

1276 artists = sum([c.findobj(matchfunc) for c in self.get_children()], []) 

1277 if include_self and matchfunc(self): 

1278 artists.append(self) 

1279 return artists 

1280 

1281 def get_cursor_data(self, event): 

1282 """ 

1283 Return the cursor data for a given event. 

1284 

1285 .. note:: 

1286 This method is intended to be overridden by artist subclasses. 

1287 As an end-user of Matplotlib you will most likely not call this 

1288 method yourself. 

1289 

1290 Cursor data can be used by Artists to provide additional context 

1291 information for a given event. The default implementation just returns 

1292 *None*. 

1293 

1294 Subclasses can override the method and return arbitrary data. However, 

1295 when doing so, they must ensure that `.format_cursor_data` can convert 

1296 the data to a string representation. 

1297 

1298 The only current use case is displaying the z-value of an `.AxesImage` 

1299 in the status bar of a plot window, while moving the mouse. 

1300 

1301 Parameters 

1302 ---------- 

1303 event : `~matplotlib.backend_bases.MouseEvent` 

1304 

1305 See Also 

1306 -------- 

1307 format_cursor_data 

1308 

1309 """ 

1310 return None 

1311 

1312 def format_cursor_data(self, data): 

1313 """ 

1314 Return a string representation of *data*. 

1315 

1316 .. note:: 

1317 This method is intended to be overridden by artist subclasses. 

1318 As an end-user of Matplotlib you will most likely not call this 

1319 method yourself. 

1320 

1321 The default implementation converts ints and floats and arrays of ints 

1322 and floats into a comma-separated string enclosed in square brackets, 

1323 unless the artist has an associated colorbar, in which case scalar 

1324 values are formatted using the colorbar's formatter. 

1325 

1326 See Also 

1327 -------- 

1328 get_cursor_data 

1329 """ 

1330 if np.ndim(data) == 0 and isinstance(self, ScalarMappable): 

1331 # This block logically belongs to ScalarMappable, but can't be 

1332 # implemented in it because most ScalarMappable subclasses inherit 

1333 # from Artist first and from ScalarMappable second, so 

1334 # Artist.format_cursor_data would always have precedence over 

1335 # ScalarMappable.format_cursor_data. 

1336 n = self.cmap.N 

1337 if np.ma.getmask(data): 

1338 return "[]" 

1339 normed = self.norm(data) 

1340 if np.isfinite(normed): 

1341 if isinstance(self.norm, BoundaryNorm): 

1342 # not an invertible normalization mapping 

1343 cur_idx = np.argmin(np.abs(self.norm.boundaries - data)) 

1344 neigh_idx = max(0, cur_idx - 1) 

1345 # use max diff to prevent delta == 0 

1346 delta = np.diff( 

1347 self.norm.boundaries[neigh_idx:cur_idx + 2] 

1348 ).max() 

1349 elif self.norm.vmin == self.norm.vmax: 

1350 # singular norms, use delta of 10% of only value 

1351 delta = np.abs(self.norm.vmin * .1) 

1352 else: 

1353 # Midpoints of neighboring color intervals. 

1354 neighbors = self.norm.inverse( 

1355 (int(normed * n) + np.array([0, 1])) / n) 

1356 delta = abs(neighbors - data).max() 

1357 g_sig_digits = cbook._g_sig_digits(data, delta) 

1358 else: 

1359 g_sig_digits = 3 # Consistent with default below. 

1360 return f"[{data:-#.{g_sig_digits}g}]" 

1361 else: 

1362 try: 

1363 data[0] 

1364 except (TypeError, IndexError): 

1365 data = [data] 

1366 data_str = ', '.join(f'{item:0.3g}' for item in data 

1367 if isinstance(item, Number)) 

1368 return "[" + data_str + "]" 

1369 

1370 def get_mouseover(self): 

1371 """ 

1372 Return whether this artist is queried for custom context information 

1373 when the mouse cursor moves over it. 

1374 """ 

1375 return self._mouseover 

1376 

1377 def set_mouseover(self, mouseover): 

1378 """ 

1379 Set whether this artist is queried for custom context information when 

1380 the mouse cursor moves over it. 

1381 

1382 Parameters 

1383 ---------- 

1384 mouseover : bool 

1385 

1386 See Also 

1387 -------- 

1388 get_cursor_data 

1389 .ToolCursorPosition 

1390 .NavigationToolbar2 

1391 """ 

1392 self._mouseover = bool(mouseover) 

1393 ax = self.axes 

1394 if ax: 

1395 if self._mouseover: 

1396 ax._mouseover_set.add(self) 

1397 else: 

1398 ax._mouseover_set.discard(self) 

1399 

1400 mouseover = property(get_mouseover, set_mouseover) # backcompat. 

1401 

1402 

1403def _get_tightbbox_for_layout_only(obj, *args, **kwargs): 

1404 """ 

1405 Matplotlib's `.Axes.get_tightbbox` and `.Axis.get_tightbbox` support a 

1406 *for_layout_only* kwarg; this helper tries to use the kwarg but skips it 

1407 when encountering third-party subclasses that do not support it. 

1408 """ 

1409 try: 

1410 return obj.get_tightbbox(*args, **{**kwargs, "for_layout_only": True}) 

1411 except TypeError: 

1412 return obj.get_tightbbox(*args, **kwargs) 

1413 

1414 

1415class ArtistInspector: 

1416 """ 

1417 A helper class to inspect an `~matplotlib.artist.Artist` and return 

1418 information about its settable properties and their current values. 

1419 """ 

1420 

1421 def __init__(self, o): 

1422 r""" 

1423 Initialize the artist inspector with an `Artist` or an iterable of 

1424 `Artist`\s. If an iterable is used, we assume it is a homogeneous 

1425 sequence (all `Artist`\s are of the same type) and it is your 

1426 responsibility to make sure this is so. 

1427 """ 

1428 if not isinstance(o, Artist): 

1429 if np.iterable(o): 

1430 o = list(o) 

1431 if len(o): 

1432 o = o[0] 

1433 

1434 self.oorig = o 

1435 if not isinstance(o, type): 

1436 o = type(o) 

1437 self.o = o 

1438 

1439 self.aliasd = self.get_aliases() 

1440 

1441 def get_aliases(self): 

1442 """ 

1443 Get a dict mapping property fullnames to sets of aliases for each alias 

1444 in the :class:`~matplotlib.artist.ArtistInspector`. 

1445 

1446 e.g., for lines:: 

1447 

1448 {'markerfacecolor': {'mfc'}, 

1449 'linewidth' : {'lw'}, 

1450 } 

1451 """ 

1452 names = [name for name in dir(self.o) 

1453 if name.startswith(('set_', 'get_')) 

1454 and callable(getattr(self.o, name))] 

1455 aliases = {} 

1456 for name in names: 

1457 func = getattr(self.o, name) 

1458 if not self.is_alias(func): 

1459 continue 

1460 propname = re.search(f"`({name[:4]}.*)`", # get_.*/set_.* 

1461 inspect.getdoc(func)).group(1) 

1462 aliases.setdefault(propname[4:], set()).add(name[4:]) 

1463 return aliases 

1464 

1465 _get_valid_values_regex = re.compile( 

1466 r"\n\s*(?:\.\.\s+)?ACCEPTS:\s*((?:.|\n)*?)(?:$|(?:\n\n))" 

1467 ) 

1468 

1469 def get_valid_values(self, attr): 

1470 """ 

1471 Get the legal arguments for the setter associated with *attr*. 

1472 

1473 This is done by querying the docstring of the setter for a line that 

1474 begins with "ACCEPTS:" or ".. ACCEPTS:", and then by looking for a 

1475 numpydoc-style documentation for the setter's first argument. 

1476 """ 

1477 

1478 name = 'set_%s' % attr 

1479 if not hasattr(self.o, name): 

1480 raise AttributeError(f'{self.o} has no function {name}') 

1481 func = getattr(self.o, name) 

1482 

1483 if hasattr(func, '_kwarg_doc'): 

1484 return func._kwarg_doc 

1485 

1486 docstring = inspect.getdoc(func) 

1487 if docstring is None: 

1488 return 'unknown' 

1489 

1490 if docstring.startswith('Alias for '): 

1491 return None 

1492 

1493 match = self._get_valid_values_regex.search(docstring) 

1494 if match is not None: 

1495 return re.sub("\n *", " ", match.group(1)) 

1496 

1497 # Much faster than list(inspect.signature(func).parameters)[1], 

1498 # although barely relevant wrt. matplotlib's total import time. 

1499 param_name = func.__code__.co_varnames[1] 

1500 # We could set the presence * based on whether the parameter is a 

1501 # varargs (it can't be a varkwargs) but it's not really worth it. 

1502 match = re.search(fr"(?m)^ *\*?{param_name} : (.+)", docstring) 

1503 if match: 

1504 return match.group(1) 

1505 

1506 return 'unknown' 

1507 

1508 def _replace_path(self, source_class): 

1509 """ 

1510 Changes the full path to the public API path that is used 

1511 in sphinx. This is needed for links to work. 

1512 """ 

1513 replace_dict = {'_base._AxesBase': 'Axes', 

1514 '_axes.Axes': 'Axes'} 

1515 for key, value in replace_dict.items(): 

1516 source_class = source_class.replace(key, value) 

1517 return source_class 

1518 

1519 def get_setters(self): 

1520 """ 

1521 Get the attribute strings with setters for object. 

1522 

1523 For example, for a line, return ``['markerfacecolor', 'linewidth', 

1524 ....]``. 

1525 """ 

1526 setters = [] 

1527 for name in dir(self.o): 

1528 if not name.startswith('set_'): 

1529 continue 

1530 func = getattr(self.o, name) 

1531 if (not callable(func) 

1532 or self.number_of_parameters(func) < 2 

1533 or self.is_alias(func)): 

1534 continue 

1535 setters.append(name[4:]) 

1536 return setters 

1537 

1538 @staticmethod 

1539 @cache 

1540 def number_of_parameters(func): 

1541 """Return number of parameters of the callable *func*.""" 

1542 return len(inspect.signature(func).parameters) 

1543 

1544 @staticmethod 

1545 @cache 

1546 def is_alias(method): 

1547 """ 

1548 Return whether the object *method* is an alias for another method. 

1549 """ 

1550 

1551 ds = inspect.getdoc(method) 

1552 if ds is None: 

1553 return False 

1554 

1555 return ds.startswith('Alias for ') 

1556 

1557 def aliased_name(self, s): 

1558 """ 

1559 Return 'PROPNAME or alias' if *s* has an alias, else return 'PROPNAME'. 

1560 

1561 For example, for the line markerfacecolor property, which has an 

1562 alias, return 'markerfacecolor or mfc' and for the transform 

1563 property, which does not, return 'transform'. 

1564 """ 

1565 aliases = ''.join(' or %s' % x for x in sorted(self.aliasd.get(s, []))) 

1566 return s + aliases 

1567 

1568 _NOT_LINKABLE = { 

1569 # A set of property setter methods that are not available in our 

1570 # current docs. This is a workaround used to prevent trying to link 

1571 # these setters which would lead to "target reference not found" 

1572 # warnings during doc build. 

1573 'matplotlib.image._ImageBase.set_alpha', 

1574 'matplotlib.image._ImageBase.set_array', 

1575 'matplotlib.image._ImageBase.set_data', 

1576 'matplotlib.image._ImageBase.set_filternorm', 

1577 'matplotlib.image._ImageBase.set_filterrad', 

1578 'matplotlib.image._ImageBase.set_interpolation', 

1579 'matplotlib.image._ImageBase.set_interpolation_stage', 

1580 'matplotlib.image._ImageBase.set_resample', 

1581 'matplotlib.text._AnnotationBase.set_annotation_clip', 

1582 } 

1583 

1584 def aliased_name_rest(self, s, target): 

1585 """ 

1586 Return 'PROPNAME or alias' if *s* has an alias, else return 'PROPNAME', 

1587 formatted for reST. 

1588 

1589 For example, for the line markerfacecolor property, which has an 

1590 alias, return 'markerfacecolor or mfc' and for the transform 

1591 property, which does not, return 'transform'. 

1592 """ 

1593 # workaround to prevent "reference target not found" 

1594 if target in self._NOT_LINKABLE: 

1595 return f'``{s}``' 

1596 

1597 aliases = ''.join(' or %s' % x for x in sorted(self.aliasd.get(s, []))) 

1598 return f':meth:`{s} <{target}>`{aliases}' 

1599 

1600 def pprint_setters(self, prop=None, leadingspace=2): 

1601 """ 

1602 If *prop* is *None*, return a list of strings of all settable 

1603 properties and their valid values. 

1604 

1605 If *prop* is not *None*, it is a valid property name and that 

1606 property will be returned as a string of property : valid 

1607 values. 

1608 """ 

1609 if leadingspace: 

1610 pad = ' ' * leadingspace 

1611 else: 

1612 pad = '' 

1613 if prop is not None: 

1614 accepts = self.get_valid_values(prop) 

1615 return f'{pad}{prop}: {accepts}' 

1616 

1617 lines = [] 

1618 for prop in sorted(self.get_setters()): 

1619 accepts = self.get_valid_values(prop) 

1620 name = self.aliased_name(prop) 

1621 lines.append(f'{pad}{name}: {accepts}') 

1622 return lines 

1623 

1624 def pprint_setters_rest(self, prop=None, leadingspace=4): 

1625 """ 

1626 If *prop* is *None*, return a list of reST-formatted strings of all 

1627 settable properties and their valid values. 

1628 

1629 If *prop* is not *None*, it is a valid property name and that 

1630 property will be returned as a string of "property : valid" 

1631 values. 

1632 """ 

1633 if leadingspace: 

1634 pad = ' ' * leadingspace 

1635 else: 

1636 pad = '' 

1637 if prop is not None: 

1638 accepts = self.get_valid_values(prop) 

1639 return f'{pad}{prop}: {accepts}' 

1640 

1641 prop_and_qualnames = [] 

1642 for prop in sorted(self.get_setters()): 

1643 # Find the parent method which actually provides the docstring. 

1644 for cls in self.o.__mro__: 

1645 method = getattr(cls, f"set_{prop}", None) 

1646 if method and method.__doc__ is not None: 

1647 break 

1648 else: # No docstring available. 

1649 method = getattr(self.o, f"set_{prop}") 

1650 prop_and_qualnames.append( 

1651 (prop, f"{method.__module__}.{method.__qualname__}")) 

1652 

1653 names = [self.aliased_name_rest(prop, target) 

1654 .replace('_base._AxesBase', 'Axes') 

1655 .replace('_axes.Axes', 'Axes') 

1656 for prop, target in prop_and_qualnames] 

1657 accepts = [self.get_valid_values(prop) 

1658 for prop, _ in prop_and_qualnames] 

1659 

1660 col0_len = max(len(n) for n in names) 

1661 col1_len = max(len(a) for a in accepts) 

1662 table_formatstr = pad + ' ' + '=' * col0_len + ' ' + '=' * col1_len 

1663 

1664 return [ 

1665 '', 

1666 pad + '.. table::', 

1667 pad + ' :class: property-table', 

1668 '', 

1669 table_formatstr, 

1670 pad + ' ' + 'Property'.ljust(col0_len) 

1671 + ' ' + 'Description'.ljust(col1_len), 

1672 table_formatstr, 

1673 *[pad + ' ' + n.ljust(col0_len) + ' ' + a.ljust(col1_len) 

1674 for n, a in zip(names, accepts)], 

1675 table_formatstr, 

1676 '', 

1677 ] 

1678 

1679 def properties(self): 

1680 """Return a dictionary mapping property name -> value.""" 

1681 o = self.oorig 

1682 getters = [name for name in dir(o) 

1683 if name.startswith('get_') and callable(getattr(o, name))] 

1684 getters.sort() 

1685 d = {} 

1686 for name in getters: 

1687 func = getattr(o, name) 

1688 if self.is_alias(func): 

1689 continue 

1690 try: 

1691 with warnings.catch_warnings(): 

1692 warnings.simplefilter('ignore') 

1693 val = func() 

1694 except Exception: 

1695 continue 

1696 else: 

1697 d[name[4:]] = val 

1698 return d 

1699 

1700 def pprint_getters(self): 

1701 """Return the getters and actual values as list of strings.""" 

1702 lines = [] 

1703 for name, val in sorted(self.properties().items()): 

1704 if getattr(val, 'shape', ()) != () and len(val) > 6: 

1705 s = str(val[:6]) + '...' 

1706 else: 

1707 s = str(val) 

1708 s = s.replace('\n', ' ') 

1709 if len(s) > 50: 

1710 s = s[:50] + '...' 

1711 name = self.aliased_name(name) 

1712 lines.append(f' {name} = {s}') 

1713 return lines 

1714 

1715 

1716def getp(obj, property=None): 

1717 """ 

1718 Return the value of an `.Artist`'s *property*, or print all of them. 

1719 

1720 Parameters 

1721 ---------- 

1722 obj : `~matplotlib.artist.Artist` 

1723 The queried artist; e.g., a `.Line2D`, a `.Text`, or an `~.axes.Axes`. 

1724 

1725 property : str or None, default: None 

1726 If *property* is 'somename', this function returns 

1727 ``obj.get_somename()``. 

1728 

1729 If it's None (or unset), it *prints* all gettable properties from 

1730 *obj*. Many properties have aliases for shorter typing, e.g. 'lw' is 

1731 an alias for 'linewidth'. In the output, aliases and full property 

1732 names will be listed as: 

1733 

1734 property or alias = value 

1735 

1736 e.g.: 

1737 

1738 linewidth or lw = 2 

1739 

1740 See Also 

1741 -------- 

1742 setp 

1743 """ 

1744 if property is None: 

1745 insp = ArtistInspector(obj) 

1746 ret = insp.pprint_getters() 

1747 print('\n'.join(ret)) 

1748 return 

1749 return getattr(obj, 'get_' + property)() 

1750 

1751# alias 

1752get = getp 

1753 

1754 

1755def setp(obj, *args, file=None, **kwargs): 

1756 """ 

1757 Set one or more properties on an `.Artist`, or list allowed values. 

1758 

1759 Parameters 

1760 ---------- 

1761 obj : `~matplotlib.artist.Artist` or list of `.Artist` 

1762 The artist(s) whose properties are being set or queried. When setting 

1763 properties, all artists are affected; when querying the allowed values, 

1764 only the first instance in the sequence is queried. 

1765 

1766 For example, two lines can be made thicker and red with a single call: 

1767 

1768 >>> x = arange(0, 1, 0.01) 

1769 >>> lines = plot(x, sin(2*pi*x), x, sin(4*pi*x)) 

1770 >>> setp(lines, linewidth=2, color='r') 

1771 

1772 file : file-like, default: `sys.stdout` 

1773 Where `setp` writes its output when asked to list allowed values. 

1774 

1775 >>> with open('output.log') as file: 

1776 ... setp(line, file=file) 

1777 

1778 The default, ``None``, means `sys.stdout`. 

1779 

1780 *args, **kwargs 

1781 The properties to set. The following combinations are supported: 

1782 

1783 - Set the linestyle of a line to be dashed: 

1784 

1785 >>> line, = plot([1, 2, 3]) 

1786 >>> setp(line, linestyle='--') 

1787 

1788 - Set multiple properties at once: 

1789 

1790 >>> setp(line, linewidth=2, color='r') 

1791 

1792 - List allowed values for a line's linestyle: 

1793 

1794 >>> setp(line, 'linestyle') 

1795 linestyle: {'-', '--', '-.', ':', '', (offset, on-off-seq), ...} 

1796 

1797 - List all properties that can be set, and their allowed values: 

1798 

1799 >>> setp(line) 

1800 agg_filter: a filter function, ... 

1801 [long output listing omitted] 

1802 

1803 `setp` also supports MATLAB style string/value pairs. For example, the 

1804 following are equivalent: 

1805 

1806 >>> setp(lines, 'linewidth', 2, 'color', 'r') # MATLAB style 

1807 >>> setp(lines, linewidth=2, color='r') # Python style 

1808 

1809 See Also 

1810 -------- 

1811 getp 

1812 """ 

1813 

1814 if isinstance(obj, Artist): 

1815 objs = [obj] 

1816 else: 

1817 objs = list(cbook.flatten(obj)) 

1818 

1819 if not objs: 

1820 return 

1821 

1822 insp = ArtistInspector(objs[0]) 

1823 

1824 if not kwargs and len(args) < 2: 

1825 if args: 

1826 print(insp.pprint_setters(prop=args[0]), file=file) 

1827 else: 

1828 print('\n'.join(insp.pprint_setters()), file=file) 

1829 return 

1830 

1831 if len(args) % 2: 

1832 raise ValueError('The set args must be string, value pairs') 

1833 

1834 funcvals = dict(zip(args[::2], args[1::2])) 

1835 ret = [o.update(funcvals) for o in objs] + [o.set(**kwargs) for o in objs] 

1836 return list(cbook.flatten(ret)) 

1837 

1838 

1839def kwdoc(artist): 

1840 r""" 

1841 Inspect an `~matplotlib.artist.Artist` class (using `.ArtistInspector`) and 

1842 return information about its settable properties and their current values. 

1843 

1844 Parameters 

1845 ---------- 

1846 artist : `~matplotlib.artist.Artist` or an iterable of `Artist`\s 

1847 

1848 Returns 

1849 ------- 

1850 str 

1851 The settable properties of *artist*, as plain text if 

1852 :rc:`docstring.hardcopy` is False and as a rst table (intended for 

1853 use in Sphinx) if it is True. 

1854 """ 

1855 ai = ArtistInspector(artist) 

1856 return ('\n'.join(ai.pprint_setters_rest(leadingspace=4)) 

1857 if mpl.rcParams['docstring.hardcopy'] else 

1858 'Properties:\n' + '\n'.join(ai.pprint_setters(leadingspace=4))) 

1859 

1860# We defer this to the end of them module, because it needs ArtistInspector 

1861# to be defined. 

1862Artist._update_set_signature_and_docstring()