Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from collections import OrderedDict, namedtuple 

2from functools import wraps 

3import inspect 

4import logging 

5from numbers import Number 

6import re 

7import warnings 

8 

9import numpy as np 

10 

11import matplotlib 

12from . import cbook, docstring, rcParams 

13from .path import Path 

14from .transforms import (Bbox, IdentityTransform, Transform, TransformedBbox, 

15 TransformedPatchPath, TransformedPath) 

16 

17_log = logging.getLogger(__name__) 

18 

19 

20def allow_rasterization(draw): 

21 """ 

22 Decorator for Artist.draw method. Provides routines 

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

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

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

26 renderer. 

27 """ 

28 

29 # the axes class has a second argument inframe for its draw method. 

30 @wraps(draw) 

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

32 try: 

33 if artist.get_rasterized(): 

34 renderer.start_rasterizing() 

35 if artist.get_agg_filter() is not None: 

36 renderer.start_filter() 

37 

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

39 finally: 

40 if artist.get_agg_filter() is not None: 

41 renderer.stop_filter(artist.get_agg_filter()) 

42 if artist.get_rasterized(): 

43 renderer.stop_rasterizing() 

44 

45 draw_wrapper._supports_rasterization = True 

46 return draw_wrapper 

47 

48 

49def _stale_axes_callback(self, val): 

50 if self.axes: 

51 self.axes.stale = val 

52 

53 

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

55 

56 

57class Artist: 

58 """ 

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

60 

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

62 """ 

63 @cbook.deprecated("3.1") 

64 @property 

65 def aname(self): 

66 return 'Artist' 

67 

68 zorder = 0 

69 # order of precedence when bulk setting/updating properties 

70 # via update. The keys should be property names and the values 

71 # integers 

72 _prop_order = dict(color=-1) 

73 

74 def __init__(self): 

75 self._stale = True 

76 self.stale_callback = None 

77 self._axes = None 

78 self.figure = None 

79 

80 self._transform = None 

81 self._transformSet = False 

82 self._visible = True 

83 self._animated = False 

84 self._alpha = None 

85 self.clipbox = None 

86 self._clippath = None 

87 self._clipon = True 

88 self._label = '' 

89 self._picker = None 

90 self._contains = None 

91 self._rasterized = None 

92 self._agg_filter = None 

93 self._mouseover = False 

94 self.eventson = False # fire events only if eventson 

95 self._oid = 0 # an observer id 

96 self._propobservers = {} # a dict from oids to funcs 

97 try: 

98 self.axes = None 

99 except AttributeError: 

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

101 pass 

102 self._remove_method = None 

103 self._url = None 

104 self._gid = None 

105 self._snap = None 

106 self._sketch = rcParams['path.sketch'] 

107 self._path_effects = rcParams['path.effects'] 

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

109 self._in_layout = True 

110 

111 def __getstate__(self): 

112 d = self.__dict__.copy() 

113 # remove the unpicklable remove method, this will get re-added on load 

114 # (by the axes) if the artist lives on an axes. 

115 d['stale_callback'] = None 

116 return d 

117 

118 def remove(self): 

119 """ 

120 Remove the artist from the figure if possible. 

121 

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

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

124 update the axes limits if desired. 

125 

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

127 collection was added to the axes with *autolim* = True. 

128 

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

130 """ 

131 

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

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

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

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

136 if self._remove_method is not None: 

137 self._remove_method(self) 

138 # clear stale callback 

139 self.stale_callback = None 

140 _ax_flag = False 

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

142 # remove from the mouse hit list 

143 self.axes._mouseover_set.discard(self) 

144 # mark the axes as stale 

145 self.axes.stale = True 

146 # decouple the artist from the axes 

147 self.axes = None 

148 _ax_flag = True 

149 

150 if self.figure: 

151 self.figure = None 

152 if not _ax_flag: 

153 self.figure = True 

154 

155 else: 

156 raise NotImplementedError('cannot remove artist') 

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

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

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

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

161 # TODO: add legend support 

162 

163 def have_units(self): 

164 """Return *True* if units are set on any axis.""" 

165 ax = self.axes 

166 return ax and any(axis.have_units() for axis in ax._get_axis_list()) 

167 

168 def convert_xunits(self, x): 

169 """ 

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

171 

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

173 have units, *x* itself is returned. 

174 """ 

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

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

177 return x 

178 return ax.xaxis.convert_units(x) 

179 

180 def convert_yunits(self, y): 

181 """ 

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

183 

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

185 have units, *y* itself is returned. 

186 """ 

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

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

189 return y 

190 return ax.yaxis.convert_units(y) 

191 

192 @property 

193 def axes(self): 

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

195 return self._axes 

196 

197 @axes.setter 

198 def axes(self, new_axes): 

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

200 and new_axes != self._axes): 

201 raise ValueError("Can not reset the axes. You are probably " 

202 "trying to re-use an artist in more than one " 

203 "Axes which is not supported") 

204 self._axes = new_axes 

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

206 self.stale_callback = _stale_axes_callback 

207 return new_axes 

208 

209 @property 

210 def stale(self): 

211 """ 

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

213 to match the internal state of the artist. 

214 """ 

215 return self._stale 

216 

217 @stale.setter 

218 def stale(self, val): 

219 self._stale = val 

220 

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

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

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

224 if self.get_animated(): 

225 return 

226 

227 if val and self.stale_callback is not None: 

228 self.stale_callback(self, val) 

229 

230 def get_window_extent(self, renderer): 

231 """ 

232 Get the axes bounding box in display space. 

233 

234 The bounding box' width and height are nonnegative. 

235 

236 Subclasses should override for inclusion in the bounding box 

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

238 box at 0, 0. 

239 

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

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

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

243 changing the axes limits, the figure size, or the canvas used 

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

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

246 but will save incorrectly. 

247 """ 

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

249 

250 def _get_clipping_extent_bbox(self): 

251 """ 

252 Return a bbox with the extents of the intersection of the clip_path 

253 and clip_box for this artist, or None if both of these are 

254 None, or ``get_clip_on`` is False. 

255 """ 

256 bbox = None 

257 if self.get_clip_on(): 

258 clip_box = self.get_clip_box() 

259 if clip_box is not None: 

260 bbox = clip_box 

261 clip_path = self.get_clip_path() 

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

263 clip_path = clip_path.get_fully_transformed_path() 

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

265 return bbox 

266 

267 def get_tightbbox(self, renderer): 

268 """ 

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

270 

271 Parameters 

272 ---------- 

273 renderer : `.RendererBase` instance 

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

275 ``fig.canvas.get_renderer()``) 

276 

277 Returns 

278 ------- 

279 bbox : `.Bbox` 

280 The enclosing bounding box (in figure pixel co-ordinates). 

281 """ 

282 bbox = self.get_window_extent(renderer) 

283 if self.get_clip_on(): 

284 clip_box = self.get_clip_box() 

285 if clip_box is not None: 

286 bbox = Bbox.intersection(bbox, clip_box) 

287 clip_path = self.get_clip_path() 

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

289 clip_path = clip_path.get_fully_transformed_path() 

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

291 return bbox 

292 

293 def add_callback(self, func): 

294 """ 

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

296 `.Artist`'s properties changes. 

297 

298 Parameters 

299 ---------- 

300 func : callable 

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

302 

303 def func(artist: Artist) -> Any 

304 

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

306 but are ignored. 

307 

308 Returns 

309 ------- 

310 oid : int 

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

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

313 

314 See Also 

315 -------- 

316 remove_callback 

317 """ 

318 oid = self._oid 

319 self._propobservers[oid] = func 

320 self._oid += 1 

321 return oid 

322 

323 def remove_callback(self, oid): 

324 """ 

325 Remove a callback based on its observer id. 

326 

327 See Also 

328 -------- 

329 add_callback 

330 """ 

331 try: 

332 del self._propobservers[oid] 

333 except KeyError: 

334 pass 

335 

336 def pchanged(self): 

337 """ 

338 Call all of the registered callbacks. 

339 

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

341 

342 See Also 

343 -------- 

344 add_callback 

345 remove_callback 

346 """ 

347 for oid, func in self._propobservers.items(): 

348 func(self) 

349 

350 def is_transform_set(self): 

351 """ 

352 Return whether the Artist has an explicitly set transform. 

353 

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

355 """ 

356 return self._transformSet 

357 

358 def set_transform(self, t): 

359 """ 

360 Set the artist transform. 

361 

362 Parameters 

363 ---------- 

364 t : `.Transform` 

365 """ 

366 self._transform = t 

367 self._transformSet = True 

368 self.pchanged() 

369 self.stale = True 

370 

371 def get_transform(self): 

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

373 if self._transform is None: 

374 self._transform = IdentityTransform() 

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

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

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

378 return self._transform 

379 

380 def get_children(self): 

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

382 return [] 

383 

384 def _default_contains(self, mouseevent, figure=None): 

385 """ 

386 Base impl. for checking whether a mouseevent happened in an artist. 

387 

388 1. If the artist defines a custom checker, use it. 

389 2. If the artist figure is known and the event did not occur in that 

390 figure (by checking its ``canvas`` attribute), reject it. 

391 3. Otherwise, return `None, {}`, indicating that the subclass' 

392 implementation should be used. 

393 

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

395 

396 inside, info = self._default_contains(mouseevent) 

397 if inside is not None: 

398 return inside, info 

399 # subclass-specific implementation follows 

400 

401 The `canvas` kwarg is provided for the implementation of 

402 `Figure.contains`. 

403 """ 

404 if callable(self._contains): 

405 return self._contains(self, mouseevent) 

406 if figure is not None and mouseevent.canvas is not figure.canvas: 

407 return False, {} 

408 return None, {} 

409 

410 def contains(self, mouseevent): 

411 """Test whether the artist contains the mouse event. 

412 

413 Parameters 

414 ---------- 

415 mouseevent : `matplotlib.backend_bases.MouseEvent` 

416 

417 Returns 

418 ------- 

419 contains : bool 

420 Whether any values are within the radius. 

421 details : dict 

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

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

424 individual Artist subclasses for details. 

425 

426 See Also 

427 -------- 

428 set_contains, get_contains 

429 """ 

430 inside, info = self._default_contains(mouseevent) 

431 if inside is not None: 

432 return inside, info 

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

434 return False, {} 

435 

436 def set_contains(self, picker): 

437 """ 

438 Define a custom contains test for the artist. 

439 

440 The provided callable replaces the default `.contains` method 

441 of the artist. 

442 

443 Parameters 

444 ---------- 

445 picker : callable 

446 A custom picker function to evaluate if an event is within the 

447 artist. The function must have the signature:: 

448 

449 def contains(artist: Artist, event: MouseEvent) -> bool, dict 

450 

451 that returns: 

452 

453 - a bool indicating if the event is within the artist 

454 - a dict of additional information. The dict should at least 

455 return the same information as the default ``contains()`` 

456 implementation of the respective artist, but may provide 

457 additional information. 

458 """ 

459 if not callable(picker): 

460 raise TypeError("picker is not a callable") 

461 self._contains = picker 

462 

463 def get_contains(self): 

464 """ 

465 Return the custom contains function of the artist if set, or *None*. 

466 

467 See Also 

468 -------- 

469 set_contains 

470 """ 

471 return self._contains 

472 

473 def pickable(self): 

474 """ 

475 Return whether the artist is pickable. 

476 

477 See Also 

478 -------- 

479 set_picker, get_picker, pick 

480 """ 

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

482 

483 def pick(self, mouseevent): 

484 """ 

485 Process a pick event. 

486 

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

488 the artist and the artist has picker set. 

489 

490 See Also 

491 -------- 

492 set_picker, get_picker, pickable 

493 """ 

494 # Pick self 

495 if self.pickable(): 

496 picker = self.get_picker() 

497 if callable(picker): 

498 inside, prop = picker(self, mouseevent) 

499 else: 

500 inside, prop = self.contains(mouseevent) 

501 if inside: 

502 self.figure.canvas.pick_event(mouseevent, self, **prop) 

503 

504 # Pick children 

505 for a in self.get_children(): 

506 # make sure the event happened in the same axes 

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

508 if (mouseevent.inaxes is None or ax is None 

509 or mouseevent.inaxes == ax): 

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

511 # because some objects associated with an axes (e.g., a 

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

513 # axes and inaxes will be None 

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

515 # which do no have an axes property but children might 

516 a.pick(mouseevent) 

517 

518 def set_picker(self, picker): 

519 """ 

520 Define the picking behavior of the artist. 

521 

522 Parameters 

523 ---------- 

524 picker : None or bool or float or callable 

525 This can be one of the following: 

526 

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

528 

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

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

531 the artist. 

532 

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

534 epsilon tolerance in points and the artist will fire 

535 off an event if it's data is within epsilon of the mouse 

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

537 the artist may provide additional data to the pick event 

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

539 epsilon of the pick event 

540 

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

542 function which determines whether the artist is hit by the 

543 mouse event:: 

544 

545 hit, props = picker(artist, mouseevent) 

546 

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

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

549 properties you want added to the PickEvent attributes. 

550 

551 """ 

552 self._picker = picker 

553 

554 def get_picker(self): 

555 """ 

556 Return the picking behavior of the artist. 

557 

558 The possible values are described in `.set_picker`. 

559 

560 See Also 

561 -------- 

562 set_picker, pickable, pick 

563 """ 

564 return self._picker 

565 

566 def get_url(self): 

567 """Return the url.""" 

568 return self._url 

569 

570 def set_url(self, url): 

571 """ 

572 Set the url for the artist. 

573 

574 Parameters 

575 ---------- 

576 url : str 

577 """ 

578 self._url = url 

579 

580 def get_gid(self): 

581 """Return the group id.""" 

582 return self._gid 

583 

584 def set_gid(self, gid): 

585 """ 

586 Set the (group) id for the artist. 

587 

588 Parameters 

589 ---------- 

590 gid : str 

591 """ 

592 self._gid = gid 

593 

594 def get_snap(self): 

595 """ 

596 Returns the snap setting. 

597 

598 See `.set_snap` for details. 

599 """ 

600 if rcParams['path.snap']: 

601 return self._snap 

602 else: 

603 return False 

604 

605 def set_snap(self, snap): 

606 """ 

607 Set the snapping behavior. 

608 

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

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

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

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

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

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

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

616 black line. 

617 

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

619 

620 Parameters 

621 ---------- 

622 snap : bool or None 

623 Possible values: 

624 

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

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

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

628 segments, round to the nearest pixel center. 

629 """ 

630 self._snap = snap 

631 self.stale = True 

632 

633 def get_sketch_params(self): 

634 """ 

635 Returns the sketch parameters for the artist. 

636 

637 Returns 

638 ------- 

639 sketch_params : tuple or None 

640 

641 A 3-tuple with the following elements: 

642 

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

644 source line. 

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

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

647 shrunken or expanded. 

648 

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

650 """ 

651 return self._sketch 

652 

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

654 """ 

655 Sets the sketch parameters. 

656 

657 Parameters 

658 ---------- 

659 scale : float, optional 

660 The amplitude of the wiggle perpendicular to the source 

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

662 sketch filter will be provided. 

663 length : float, optional 

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

665 (default 128.0) 

666 randomness : float, optional 

667 The scale factor by which the length is shrunken or 

668 expanded (default 16.0) 

669 

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

671 """ 

672 if scale is None: 

673 self._sketch = None 

674 else: 

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

676 self.stale = True 

677 

678 def set_path_effects(self, path_effects): 

679 """Set the path effects. 

680 

681 Parameters 

682 ---------- 

683 path_effects : `.AbstractPathEffect` 

684 """ 

685 self._path_effects = path_effects 

686 self.stale = True 

687 

688 def get_path_effects(self): 

689 return self._path_effects 

690 

691 def get_figure(self): 

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

693 return self.figure 

694 

695 def set_figure(self, fig): 

696 """ 

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

698 

699 Parameters 

700 ---------- 

701 fig : `.Figure` 

702 """ 

703 # if this is a no-op just return 

704 if self.figure is fig: 

705 return 

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

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

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

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

710 # to more than one Axes 

711 if self.figure is not None: 

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

713 "more than one figure") 

714 self.figure = fig 

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

716 self.pchanged() 

717 self.stale = True 

718 

719 def set_clip_box(self, clipbox): 

720 """ 

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

722 

723 Parameters 

724 ---------- 

725 clipbox : `.Bbox` 

726 """ 

727 self.clipbox = clipbox 

728 self.pchanged() 

729 self.stale = True 

730 

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

732 """ 

733 Set the artist's clip path. 

734 

735 Parameters 

736 ---------- 

737 path : `.Patch` or `.Path` or `.TransformedPath` or None 

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

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

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

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

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

743 

744 Notes 

745 ----- 

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

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

748 to ``None``. 

749 

750 For technical reasons (support of ``setp``), a tuple 

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

752 parameter. 

753 

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

755 """ 

756 from matplotlib.patches import Patch, Rectangle 

757 

758 success = False 

759 if transform is None: 

760 if isinstance(path, Rectangle): 

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

762 path.get_transform()) 

763 self._clippath = None 

764 success = True 

765 elif isinstance(path, Patch): 

766 self._clippath = TransformedPatchPath(path) 

767 success = True 

768 elif isinstance(path, tuple): 

769 path, transform = path 

770 

771 if path is None: 

772 self._clippath = None 

773 success = True 

774 elif isinstance(path, Path): 

775 self._clippath = TransformedPath(path, transform) 

776 success = True 

777 elif isinstance(path, TransformedPatchPath): 

778 self._clippath = path 

779 success = True 

780 elif isinstance(path, TransformedPath): 

781 self._clippath = path 

782 success = True 

783 

784 if not success: 

785 raise TypeError( 

786 "Invalid arguments to set_clip_path, of type {} and {}" 

787 .format(type(path).__name__, type(transform).__name__)) 

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

789 # will be hit at least once. 

790 self.pchanged() 

791 self.stale = True 

792 

793 def get_alpha(self): 

794 """ 

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

796 backends 

797 """ 

798 return self._alpha 

799 

800 def get_visible(self): 

801 """Return the visibility.""" 

802 return self._visible 

803 

804 def get_animated(self): 

805 """Return the animated state.""" 

806 return self._animated 

807 

808 def get_in_layout(self): 

809 """ 

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

811 calculations. 

812 

813 E.g. :doc:`/tutorials/intermediate/constrainedlayout_guide`, 

814 `.Figure.tight_layout()`, and 

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

816 """ 

817 return self._in_layout 

818 

819 def get_clip_on(self): 

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

821 return self._clipon 

822 

823 def get_clip_box(self): 

824 """Return the clipbox.""" 

825 return self.clipbox 

826 

827 def get_clip_path(self): 

828 """Return the clip path.""" 

829 return self._clippath 

830 

831 def get_transformed_clip_path_and_affine(self): 

832 ''' 

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

834 transformation applied, and the remaining affine part of its 

835 transformation. 

836 ''' 

837 if self._clippath is not None: 

838 return self._clippath.get_transformed_path_and_affine() 

839 return None, None 

840 

841 def set_clip_on(self, b): 

842 """ 

843 Set whether the artist uses clipping. 

844 

845 When False artists will be visible out side of the axes which 

846 can lead to unexpected results. 

847 

848 Parameters 

849 ---------- 

850 b : bool 

851 """ 

852 self._clipon = b 

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

854 # are hit at least once 

855 self.pchanged() 

856 self.stale = True 

857 

858 def _set_gc_clip(self, gc): 

859 'Set the clip properly for the gc' 

860 if self._clipon: 

861 if self.clipbox is not None: 

862 gc.set_clip_rectangle(self.clipbox) 

863 gc.set_clip_path(self._clippath) 

864 else: 

865 gc.set_clip_rectangle(None) 

866 gc.set_clip_path(None) 

867 

868 def get_rasterized(self): 

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

870 return self._rasterized 

871 

872 def set_rasterized(self, rasterized): 

873 """ 

874 Force rasterized (bitmap) drawing in vector backend output. 

875 

876 Defaults to None, which implies the backend's default behavior. 

877 

878 Parameters 

879 ---------- 

880 rasterized : bool or None 

881 """ 

882 if rasterized and not hasattr(self.draw, "_supports_rasterization"): 

883 cbook._warn_external( 

884 "Rasterization of '%s' will be ignored" % self) 

885 

886 self._rasterized = rasterized 

887 

888 def get_agg_filter(self): 

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

890 return self._agg_filter 

891 

892 def set_agg_filter(self, filter_func): 

893 """Set the agg filter. 

894 

895 Parameters 

896 ---------- 

897 filter_func : callable 

898 A filter function, which takes a (m, n, 3) float array and a dpi 

899 value, and returns a (m, n, 3) array. 

900 

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

902 and a dpi value, and returns a (m, n, 3) array 

903 """ 

904 self._agg_filter = filter_func 

905 self.stale = True 

906 

907 def draw(self, renderer, *args, **kwargs): 

908 """ 

909 Draw the Artist using the given renderer. 

910 

911 This method will be overridden in the Artist subclasses. Typically, 

912 it is implemented to not have any effect if the Artist is not visible 

913 (`.Artist.get_visible` is *False*). 

914 

915 Parameters 

916 ---------- 

917 renderer : `.RendererBase` subclass. 

918 """ 

919 if not self.get_visible(): 

920 return 

921 self.stale = False 

922 

923 def set_alpha(self, alpha): 

924 """ 

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

926 

927 Parameters 

928 ---------- 

929 alpha : float or None 

930 """ 

931 if alpha is not None and not isinstance(alpha, Number): 

932 raise TypeError('alpha must be a float or None') 

933 self._alpha = alpha 

934 self.pchanged() 

935 self.stale = True 

936 

937 def set_visible(self, b): 

938 """ 

939 Set the artist's visibility. 

940 

941 Parameters 

942 ---------- 

943 b : bool 

944 """ 

945 self._visible = b 

946 self.pchanged() 

947 self.stale = True 

948 

949 def set_animated(self, b): 

950 """ 

951 Set the artist's animation state. 

952 

953 Parameters 

954 ---------- 

955 b : bool 

956 """ 

957 if self._animated != b: 

958 self._animated = b 

959 self.pchanged() 

960 

961 def set_in_layout(self, in_layout): 

962 """ 

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

964 E.g. :doc:`/tutorials/intermediate/constrainedlayout_guide`, 

965 `.Figure.tight_layout()`, and 

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

967 

968 Parameters 

969 ---------- 

970 in_layout : bool 

971 """ 

972 self._in_layout = in_layout 

973 

974 def update(self, props): 

975 """ 

976 Update this artist's properties from the dictionary *props*. 

977 """ 

978 def _update_property(self, k, v): 

979 """Sorting out how to update property (setter or setattr). 

980 

981 Parameters 

982 ---------- 

983 k : str 

984 The name of property to update 

985 v : obj 

986 The value to assign to the property 

987 

988 Returns 

989 ------- 

990 ret : obj or None 

991 If using a `set_*` method return it's return, else None. 

992 """ 

993 k = k.lower() 

994 # white list attributes we want to be able to update through 

995 # art.update, art.set, setp 

996 if k in {'axes'}: 

997 return setattr(self, k, v) 

998 else: 

999 func = getattr(self, 'set_' + k, None) 

1000 if not callable(func): 

1001 raise AttributeError('{!r} object has no property {!r}' 

1002 .format(type(self).__name__, k)) 

1003 return func(v) 

1004 

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

1006 ret = [_update_property(self, k, v) for k, v in props.items()] 

1007 

1008 if len(ret): 

1009 self.pchanged() 

1010 self.stale = True 

1011 return ret 

1012 

1013 def get_label(self): 

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

1015 return self._label 

1016 

1017 def set_label(self, s): 

1018 """ 

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

1020 

1021 Parameters 

1022 ---------- 

1023 s : object 

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

1025 """ 

1026 if s is not None: 

1027 self._label = str(s) 

1028 else: 

1029 self._label = None 

1030 self.pchanged() 

1031 self.stale = True 

1032 

1033 def get_zorder(self): 

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

1035 return self.zorder 

1036 

1037 def set_zorder(self, level): 

1038 """ 

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

1040 values are drawn first. 

1041 

1042 Parameters 

1043 ---------- 

1044 level : float 

1045 """ 

1046 if level is None: 

1047 level = self.__class__.zorder 

1048 self.zorder = level 

1049 self.pchanged() 

1050 self.stale = True 

1051 

1052 @property 

1053 def sticky_edges(self): 

1054 """ 

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

1056 

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

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

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

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

1061 histogram. 

1062 

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

1064 lists can be modified in place as needed. 

1065 

1066 Examples 

1067 -------- 

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

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

1070 

1071 """ 

1072 return self._sticky_edges 

1073 

1074 def update_from(self, other): 

1075 'Copy properties from *other* to *self*.' 

1076 self._transform = other._transform 

1077 self._transformSet = other._transformSet 

1078 self._visible = other._visible 

1079 self._alpha = other._alpha 

1080 self.clipbox = other.clipbox 

1081 self._clipon = other._clipon 

1082 self._clippath = other._clippath 

1083 self._label = other._label 

1084 self._sketch = other._sketch 

1085 self._path_effects = other._path_effects 

1086 self.sticky_edges.x[:] = other.sticky_edges.x[:] 

1087 self.sticky_edges.y[:] = other.sticky_edges.y[:] 

1088 self.pchanged() 

1089 self.stale = True 

1090 

1091 def properties(self): 

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

1093 return ArtistInspector(self).properties() 

1094 

1095 def set(self, **kwargs): 

1096 """A property batch setter. Pass *kwargs* to set properties.""" 

1097 kwargs = cbook.normalize_kwargs(kwargs, self) 

1098 props = OrderedDict( 

1099 sorted(kwargs.items(), reverse=True, 

1100 key=lambda x: (self._prop_order.get(x[0], 0), x[0]))) 

1101 return self.update(props) 

1102 

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

1104 """ 

1105 Find artist objects. 

1106 

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

1108 

1109 Parameters 

1110 ---------- 

1111 match 

1112 A filter criterion for the matches. This can be 

1113 

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

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

1116 The result will only contain artists for which the function 

1117 returns *True*. 

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

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

1120 

1121 include_self : bool 

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

1123 

1124 Returns 

1125 ------- 

1126 artists : list of `.Artist` 

1127 

1128 """ 

1129 if match is None: # always return True 

1130 def matchfunc(x): 

1131 return True 

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

1133 def matchfunc(x): 

1134 return isinstance(x, match) 

1135 elif callable(match): 

1136 matchfunc = match 

1137 else: 

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

1139 'subclass, or a callable') 

1140 

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

1142 if include_self and matchfunc(self): 

1143 artists.append(self) 

1144 return artists 

1145 

1146 def get_cursor_data(self, event): 

1147 """ 

1148 Return the cursor data for a given event. 

1149 

1150 .. note:: 

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

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

1153 method yourself. 

1154 

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

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

1157 *None*. 

1158 

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

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

1161 the data to a string representation. 

1162 

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

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

1165 

1166 Parameters 

1167 ---------- 

1168 event : `matplotlib.backend_bases.MouseEvent` 

1169 

1170 See Also 

1171 -------- 

1172 format_cursor_data 

1173 

1174 """ 

1175 return None 

1176 

1177 def format_cursor_data(self, data): 

1178 """ 

1179 Return a string representation of *data*. 

1180 

1181 .. note:: 

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

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

1184 method yourself. 

1185 

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

1187 and floats into a comma-separated string enclosed in square brackets. 

1188 

1189 See Also 

1190 -------- 

1191 get_cursor_data 

1192 """ 

1193 try: 

1194 data[0] 

1195 except (TypeError, IndexError): 

1196 data = [data] 

1197 data_str = ', '.join('{:0.3g}'.format(item) for item in data 

1198 if isinstance(item, Number)) 

1199 return "[" + data_str + "]" 

1200 

1201 @property 

1202 def mouseover(self): 

1203 """ 

1204 If this property is set to *True*, the artist will be queried for 

1205 custom context information when the mouse cursor moves over it. 

1206 

1207 See also :meth:`get_cursor_data`, :class:`.ToolCursorPosition` and 

1208 :class:`.NavigationToolbar2`. 

1209 """ 

1210 return self._mouseover 

1211 

1212 @mouseover.setter 

1213 def mouseover(self, val): 

1214 val = bool(val) 

1215 self._mouseover = val 

1216 ax = self.axes 

1217 if ax: 

1218 if val: 

1219 ax._mouseover_set.add(self) 

1220 else: 

1221 ax._mouseover_set.discard(self) 

1222 

1223 

1224class ArtistInspector: 

1225 """ 

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

1227 information about its settable properties and their current values. 

1228 """ 

1229 

1230 def __init__(self, o): 

1231 r""" 

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

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

1234 sequence (all `Artists` are of the same type) and it is your 

1235 responsibility to make sure this is so. 

1236 """ 

1237 if not isinstance(o, Artist): 

1238 if np.iterable(o): 

1239 o = list(o) 

1240 if len(o): 

1241 o = o[0] 

1242 

1243 self.oorig = o 

1244 if not isinstance(o, type): 

1245 o = type(o) 

1246 self.o = o 

1247 

1248 self.aliasd = self.get_aliases() 

1249 

1250 def get_aliases(self): 

1251 """ 

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

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

1254 

1255 e.g., for lines:: 

1256 

1257 {'markerfacecolor': {'mfc'}, 

1258 'linewidth' : {'lw'}, 

1259 } 

1260 """ 

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

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

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

1264 aliases = {} 

1265 for name in names: 

1266 func = getattr(self.o, name) 

1267 if not self.is_alias(func): 

1268 continue 

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

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

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

1272 return aliases 

1273 

1274 _get_valid_values_regex = re.compile( 

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

1276 ) 

1277 

1278 def get_valid_values(self, attr): 

1279 """ 

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

1281 

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

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

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

1285 """ 

1286 

1287 name = 'set_%s' % attr 

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

1289 raise AttributeError('%s has no function %s' % (self.o, name)) 

1290 func = getattr(self.o, name) 

1291 

1292 docstring = inspect.getdoc(func) 

1293 if docstring is None: 

1294 return 'unknown' 

1295 

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

1297 return None 

1298 

1299 match = self._get_valid_values_regex.search(docstring) 

1300 if match is not None: 

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

1302 

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

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

1305 param_name = func.__code__.co_varnames[1] 

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

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

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

1309 if match: 

1310 return match.group(1) 

1311 

1312 return 'unknown' 

1313 

1314 def _get_setters_and_targets(self): 

1315 """ 

1316 Get the attribute strings and a full path to where the setter 

1317 is defined for all setters in an object. 

1318 """ 

1319 setters = [] 

1320 for name in dir(self.o): 

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

1322 continue 

1323 func = getattr(self.o, name) 

1324 if (not callable(func) 

1325 or len(inspect.signature(func).parameters) < 2 

1326 or self.is_alias(func)): 

1327 continue 

1328 setters.append( 

1329 (name[4:], f"{func.__module__}.{func.__qualname__}")) 

1330 return setters 

1331 

1332 def _replace_path(self, source_class): 

1333 """ 

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

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

1336 """ 

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

1338 '_axes.Axes': 'Axes'} 

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

1340 source_class = source_class.replace(key, value) 

1341 return source_class 

1342 

1343 def get_setters(self): 

1344 """ 

1345 Get the attribute strings with setters for object. e.g., for a line, 

1346 return ``['markerfacecolor', 'linewidth', ....]``. 

1347 """ 

1348 return [prop for prop, target in self._get_setters_and_targets()] 

1349 

1350 def is_alias(self, o): 

1351 """Return whether method object *o* is an alias for another method.""" 

1352 ds = inspect.getdoc(o) 

1353 if ds is None: 

1354 return False 

1355 return ds.startswith('Alias for ') 

1356 

1357 def aliased_name(self, s): 

1358 """ 

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

1360 

1361 e.g., for the line markerfacecolor property, which has an 

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

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

1364 """ 

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

1366 return s + aliases 

1367 

1368 def aliased_name_rest(self, s, target): 

1369 """ 

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

1371 formatted for ReST. 

1372 

1373 e.g., for the line markerfacecolor property, which has an 

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

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

1376 """ 

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

1378 return ':meth:`%s <%s>`%s' % (s, target, aliases) 

1379 

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

1381 """ 

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

1383 properties and their valid values. 

1384 

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

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

1387 values. 

1388 """ 

1389 if leadingspace: 

1390 pad = ' ' * leadingspace 

1391 else: 

1392 pad = '' 

1393 if prop is not None: 

1394 accepts = self.get_valid_values(prop) 

1395 return '%s%s: %s' % (pad, prop, accepts) 

1396 

1397 attrs = self._get_setters_and_targets() 

1398 attrs.sort() 

1399 lines = [] 

1400 

1401 for prop, path in attrs: 

1402 accepts = self.get_valid_values(prop) 

1403 name = self.aliased_name(prop) 

1404 

1405 lines.append('%s%s: %s' % (pad, name, accepts)) 

1406 return lines 

1407 

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

1409 """ 

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

1411 properties and their valid values. Format the output for ReST 

1412 

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

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

1415 values. 

1416 """ 

1417 if leadingspace: 

1418 pad = ' ' * leadingspace 

1419 else: 

1420 pad = '' 

1421 if prop is not None: 

1422 accepts = self.get_valid_values(prop) 

1423 return '%s%s: %s' % (pad, prop, accepts) 

1424 

1425 attrs = sorted(self._get_setters_and_targets()) 

1426 

1427 names = [self.aliased_name_rest(prop, target).replace( 

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

1429 '_axes.Axes', 'Axes') 

1430 for prop, target in attrs] 

1431 accepts = [self.get_valid_values(prop) for prop, target in attrs] 

1432 

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

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

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

1436 

1437 return [ 

1438 '', 

1439 pad + '.. table::', 

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

1441 '', 

1442 table_formatstr, 

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

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

1445 table_formatstr, 

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

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

1448 table_formatstr, 

1449 '', 

1450 ] 

1451 

1452 def properties(self): 

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

1454 o = self.oorig 

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

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

1457 getters.sort() 

1458 d = {} 

1459 for name in getters: 

1460 func = getattr(o, name) 

1461 if self.is_alias(func): 

1462 continue 

1463 try: 

1464 with warnings.catch_warnings(): 

1465 warnings.simplefilter('ignore') 

1466 val = func() 

1467 except Exception: 

1468 continue 

1469 else: 

1470 d[name[4:]] = val 

1471 return d 

1472 

1473 def pprint_getters(self): 

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

1475 lines = [] 

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

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

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

1479 else: 

1480 s = str(val) 

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

1482 if len(s) > 50: 

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

1484 name = self.aliased_name(name) 

1485 lines.append(' %s = %s' % (name, s)) 

1486 return lines 

1487 

1488 

1489def getp(obj, property=None): 

1490 """ 

1491 Return the value of object's property. *property* is an optional string 

1492 for the property you want to return 

1493 

1494 Example usage:: 

1495 

1496 getp(obj) # get all the object properties 

1497 getp(obj, 'linestyle') # get the linestyle property 

1498 

1499 *obj* is a :class:`Artist` instance, e.g., 

1500 :class:`~matplotlib.lines.Line2D` or an instance of a 

1501 :class:`~matplotlib.axes.Axes` or :class:`matplotlib.text.Text`. 

1502 If the *property* is 'somename', this function returns 

1503 

1504 obj.get_somename() 

1505 

1506 :func:`getp` can be used to query all the gettable properties with 

1507 ``getp(obj)``. Many properties have aliases for shorter typing, e.g. 

1508 'lw' is an alias for 'linewidth'. In the output, aliases and full 

1509 property names will be listed as: 

1510 

1511 property or alias = value 

1512 

1513 e.g.: 

1514 

1515 linewidth or lw = 2 

1516 """ 

1517 if property is None: 

1518 insp = ArtistInspector(obj) 

1519 ret = insp.pprint_getters() 

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

1521 return 

1522 

1523 func = getattr(obj, 'get_' + property) 

1524 return func() 

1525 

1526# alias 

1527get = getp 

1528 

1529 

1530def setp(obj, *args, **kwargs): 

1531 """ 

1532 Set a property on an artist object. 

1533 

1534 matplotlib supports the use of :func:`setp` ("set property") and 

1535 :func:`getp` to set and get object properties, as well as to do 

1536 introspection on the object. For example, to set the linestyle of a 

1537 line to be dashed, you can do:: 

1538 

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

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

1541 

1542 If you want to know the valid types of arguments, you can provide 

1543 the name of the property you want to set without a value:: 

1544 

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

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

1547 

1548 If you want to see all the properties that can be set, and their 

1549 possible values, you can do:: 

1550 

1551 >>> setp(line) 

1552 ... long output listing omitted 

1553 

1554 You may specify another output file to `setp` if `sys.stdout` is not 

1555 acceptable for some reason using the *file* keyword-only argument:: 

1556 

1557 >>> with fopen('output.log') as f: 

1558 >>> setp(line, file=f) 

1559 

1560 :func:`setp` operates on a single instance or a iterable of 

1561 instances. If you are in query mode introspecting the possible 

1562 values, only the first instance in the sequence is used. When 

1563 actually setting values, all the instances will be set. e.g., 

1564 suppose you have a list of two lines, the following will make both 

1565 lines thicker and red:: 

1566 

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

1568 >>> y1 = sin(2*pi*x) 

1569 >>> y2 = sin(4*pi*x) 

1570 >>> lines = plot(x, y1, x, y2) 

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

1572 

1573 :func:`setp` works with the MATLAB style string/value pairs or 

1574 with python kwargs. For example, the following are equivalent:: 

1575 

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

1577 >>> setp(lines, linewidth=2, color='r') # python style 

1578 """ 

1579 

1580 if isinstance(obj, Artist): 

1581 objs = [obj] 

1582 else: 

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

1584 

1585 if not objs: 

1586 return 

1587 

1588 insp = ArtistInspector(objs[0]) 

1589 

1590 # file has to be popped before checking if kwargs is empty 

1591 printArgs = {} 

1592 if 'file' in kwargs: 

1593 printArgs['file'] = kwargs.pop('file') 

1594 

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

1596 if args: 

1597 print(insp.pprint_setters(prop=args[0]), **printArgs) 

1598 else: 

1599 print('\n'.join(insp.pprint_setters()), **printArgs) 

1600 return 

1601 

1602 if len(args) % 2: 

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

1604 

1605 # put args into ordereddict to maintain order 

1606 funcvals = OrderedDict((k, v) for k, v in zip(args[::2], args[1::2])) 

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

1608 return list(cbook.flatten(ret)) 

1609 

1610 

1611def kwdoc(artist): 

1612 r""" 

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

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

1615 

1616 Parameters 

1617 ---------- 

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

1619 

1620 Returns 

1621 ------- 

1622 string 

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

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

1625 use in Sphinx) if it is True. 

1626 """ 

1627 ai = ArtistInspector(artist) 

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

1629 if matplotlib.rcParams['docstring.hardcopy'] else 

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

1631 

1632 

1633docstring.interpd.update(Artist=kwdoc(Artist))