Buckets:
| from collections import namedtuple | |
| import contextlib | |
| from functools import cache, reduce, wraps | |
| import inspect | |
| from inspect import Signature, Parameter | |
| import logging | |
| from numbers import Number, Real | |
| import operator | |
| import re | |
| import warnings | |
| import numpy as np | |
| import matplotlib as mpl | |
| from . import _api, cbook | |
| from .path import Path | |
| from .transforms import (BboxBase, Bbox, IdentityTransform, Transform, TransformedBbox, | |
| TransformedPatchPath, TransformedPath) | |
| _log = logging.getLogger(__name__) | |
| def _prevent_rasterization(draw): | |
| # We assume that by default artists are not allowed to rasterize (unless | |
| # its draw method is explicitly decorated). If it is being drawn after a | |
| # rasterized artist and it has reached a raster_depth of 0, we stop | |
| # rasterization so that it does not affect the behavior of normal artist | |
| # (e.g., change in dpi). | |
| def draw_wrapper(artist, renderer, *args, **kwargs): | |
| if renderer._raster_depth == 0 and renderer._rasterizing: | |
| # Only stop when we are not in a rasterized parent | |
| # and something has been rasterized since last stop. | |
| renderer.stop_rasterizing() | |
| renderer._rasterizing = False | |
| return draw(artist, renderer, *args, **kwargs) | |
| draw_wrapper._supports_rasterization = False | |
| return draw_wrapper | |
| def allow_rasterization(draw): | |
| """ | |
| Decorator for Artist.draw method. Provides routines | |
| that run before and after the draw call. The before and after functions | |
| are useful for changing artist-dependent renderer attributes or making | |
| other setup function calls, such as starting and flushing a mixed-mode | |
| renderer. | |
| """ | |
| def draw_wrapper(artist, renderer): | |
| try: | |
| if artist.get_rasterized(): | |
| if renderer._raster_depth == 0 and not renderer._rasterizing: | |
| renderer.start_rasterizing() | |
| renderer._rasterizing = True | |
| renderer._raster_depth += 1 | |
| else: | |
| if renderer._raster_depth == 0 and renderer._rasterizing: | |
| # Only stop when we are not in a rasterized parent | |
| # and something has be rasterized since last stop | |
| renderer.stop_rasterizing() | |
| renderer._rasterizing = False | |
| if artist.get_agg_filter() is not None: | |
| renderer.start_filter() | |
| return draw(artist, renderer) | |
| finally: | |
| if artist.get_agg_filter() is not None: | |
| renderer.stop_filter(artist.get_agg_filter()) | |
| if artist.get_rasterized(): | |
| renderer._raster_depth -= 1 | |
| if (renderer._rasterizing and (fig := artist.get_figure(root=True)) and | |
| fig.suppressComposite): | |
| # restart rasterizing to prevent merging | |
| renderer.stop_rasterizing() | |
| renderer.start_rasterizing() | |
| draw_wrapper._supports_rasterization = True | |
| return draw_wrapper | |
| def _finalize_rasterization(draw): | |
| """ | |
| Decorator for Artist.draw method. Needed on the outermost artist, i.e. | |
| Figure, to finish up if the render is still in rasterized mode. | |
| """ | |
| def draw_wrapper(artist, renderer, *args, **kwargs): | |
| result = draw(artist, renderer, *args, **kwargs) | |
| if renderer._rasterizing: | |
| renderer.stop_rasterizing() | |
| renderer._rasterizing = False | |
| return result | |
| return draw_wrapper | |
| def _stale_axes_callback(self, val): | |
| if self.axes: | |
| self.axes.stale = val | |
| _XYPair = namedtuple("_XYPair", "x y") | |
| class _Unset: | |
| def __repr__(self): | |
| return "<UNSET>" | |
| _UNSET = _Unset() | |
| class Artist: | |
| """ | |
| Abstract base class for objects that render into a FigureCanvas. | |
| Typically, all visible elements in a figure are subclasses of Artist. | |
| """ | |
| zorder = 0 | |
| def __init_subclass__(cls): | |
| # Decorate draw() method so that all artists are able to stop | |
| # rastrization when necessary. If the artist's draw method is already | |
| # decorated (has a `_supports_rasterization` attribute), it won't be | |
| # decorated. | |
| if not hasattr(cls.draw, "_supports_rasterization"): | |
| cls.draw = _prevent_rasterization(cls.draw) | |
| # Inject custom set() methods into the subclass with signature and | |
| # docstring based on the subclasses' properties. | |
| if not hasattr(cls.set, '_autogenerated_signature'): | |
| # Don't overwrite cls.set if the subclass or one of its parents | |
| # has defined a set method set itself. | |
| # If there was no explicit definition, cls.set is inherited from | |
| # the hierarchy of auto-generated set methods, which hold the | |
| # flag _autogenerated_signature. | |
| return | |
| cls.set = lambda self, **kwargs: Artist.set(self, **kwargs) | |
| cls.set.__name__ = "set" | |
| cls.set.__qualname__ = f"{cls.__qualname__}.set" | |
| cls._update_set_signature_and_docstring() | |
| _PROPERTIES_EXCLUDED_FROM_SET = [ | |
| 'navigate_mode', # not a user-facing function | |
| 'figure', # changing the figure is such a profound operation | |
| # that we don't want this in set() | |
| '3d_properties', # cannot be used as a keyword due to leading digit | |
| ] | |
| def _update_set_signature_and_docstring(cls): | |
| """ | |
| Update the signature of the set function to list all properties | |
| as keyword arguments. | |
| Property aliases are not listed in the signature for brevity, but | |
| are still accepted as keyword arguments. | |
| """ | |
| cls.set.__signature__ = Signature( | |
| [Parameter("self", Parameter.POSITIONAL_OR_KEYWORD), | |
| *[Parameter(prop, Parameter.KEYWORD_ONLY, default=_UNSET) | |
| for prop in ArtistInspector(cls).get_setters() | |
| if prop not in Artist._PROPERTIES_EXCLUDED_FROM_SET]]) | |
| cls.set._autogenerated_signature = True | |
| cls.set.__doc__ = ( | |
| "Set multiple properties at once.\n\n" | |
| "Supported properties are\n\n" | |
| + kwdoc(cls)) | |
| def __init__(self): | |
| self._stale = True | |
| self.stale_callback = None | |
| self._axes = None | |
| self._parent_figure = None | |
| self._transform = None | |
| self._transformSet = False | |
| self._visible = True | |
| self._animated = False | |
| self._alpha = None | |
| self.clipbox = None | |
| self._clippath = None | |
| self._clipon = True | |
| self._label = '' | |
| self._picker = None | |
| self._rasterized = False | |
| self._agg_filter = None | |
| # Normally, artist classes need to be queried for mouseover info if and | |
| # only if they override get_cursor_data. | |
| self._mouseover = type(self).get_cursor_data != Artist.get_cursor_data | |
| self._callbacks = cbook.CallbackRegistry(signals=["pchanged"]) | |
| try: | |
| self.axes = None | |
| except AttributeError: | |
| # Handle self.axes as a read-only property, as in Figure. | |
| pass | |
| self._remove_method = None | |
| self._url = None | |
| self._gid = None | |
| self._snap = None | |
| self._sketch = mpl.rcParams['path.sketch'] | |
| self._path_effects = mpl.rcParams['path.effects'] | |
| self._sticky_edges = _XYPair([], []) | |
| self._in_layout = True | |
| def __getstate__(self): | |
| d = self.__dict__.copy() | |
| d['stale_callback'] = None | |
| return d | |
| def remove(self): | |
| """ | |
| Remove the artist from the figure if possible. | |
| The effect will not be visible until the figure is redrawn, e.g., | |
| with `.FigureCanvasBase.draw_idle`. Call `~.axes.Axes.relim` to | |
| update the Axes limits if desired. | |
| Note: `~.axes.Axes.relim` will not see collections even if the | |
| collection was added to the Axes with *autolim* = True. | |
| Note: there is no support for removing the artist's legend entry. | |
| """ | |
| # There is no method to set the callback. Instead, the parent should | |
| # set the _remove_method attribute directly. This would be a | |
| # protected attribute if Python supported that sort of thing. The | |
| # callback has one parameter, which is the child to be removed. | |
| if self._remove_method is not None: | |
| self._remove_method(self) | |
| # clear stale callback | |
| self.stale_callback = None | |
| _ax_flag = False | |
| if hasattr(self, 'axes') and self.axes: | |
| # remove from the mouse hit list | |
| self.axes._mouseover_set.discard(self) | |
| self.axes.stale = True | |
| self.axes = None # decouple the artist from the Axes | |
| _ax_flag = True | |
| if (fig := self.get_figure(root=False)) is not None: | |
| if not _ax_flag: | |
| fig.stale = True | |
| self._parent_figure = None | |
| else: | |
| raise NotImplementedError('cannot remove artist') | |
| # TODO: the fix for the collections relim problem is to move the | |
| # limits calculation into the artist itself, including the property of | |
| # whether or not the artist should affect the limits. Then there will | |
| # be no distinction between axes.add_line, axes.add_patch, etc. | |
| # TODO: add legend support | |
| def have_units(self): | |
| """Return whether units are set on any axis.""" | |
| ax = self.axes | |
| return ax and any(axis.have_units() for axis in ax._axis_map.values()) | |
| def convert_xunits(self, x): | |
| """ | |
| Convert *x* using the unit type of the xaxis. | |
| If the artist is not contained in an Axes or if the xaxis does not | |
| have units, *x* itself is returned. | |
| """ | |
| ax = getattr(self, 'axes', None) | |
| if ax is None or ax.xaxis is None: | |
| return x | |
| return ax.xaxis.convert_units(x) | |
| def convert_yunits(self, y): | |
| """ | |
| Convert *y* using the unit type of the yaxis. | |
| If the artist is not contained in an Axes or if the yaxis does not | |
| have units, *y* itself is returned. | |
| """ | |
| ax = getattr(self, 'axes', None) | |
| if ax is None or ax.yaxis is None: | |
| return y | |
| return ax.yaxis.convert_units(y) | |
| def axes(self): | |
| """The `~.axes.Axes` instance the artist resides in, or *None*.""" | |
| return self._axes | |
| def axes(self, new_axes): | |
| if (new_axes is not None and self._axes is not None | |
| and new_axes != self._axes): | |
| raise ValueError("Can not reset the Axes. You are probably trying to reuse " | |
| "an artist in more than one Axes which is not supported") | |
| self._axes = new_axes | |
| if new_axes is not None and new_axes is not self: | |
| self.stale_callback = _stale_axes_callback | |
| def stale(self): | |
| """ | |
| Whether the artist is 'stale' and needs to be re-drawn for the output | |
| to match the internal state of the artist. | |
| """ | |
| return self._stale | |
| def stale(self, val): | |
| self._stale = val | |
| # if the artist is animated it does not take normal part in the | |
| # draw stack and is not expected to be drawn as part of the normal | |
| # draw loop (when not saving) so do not propagate this change | |
| if self._animated: | |
| return | |
| if val and self.stale_callback is not None: | |
| self.stale_callback(self, val) | |
| def get_window_extent(self, renderer=None): | |
| """ | |
| Get the artist's bounding box in display space. | |
| The bounding box' width and height are nonnegative. | |
| Subclasses should override for inclusion in the bounding box | |
| "tight" calculation. Default is to return an empty bounding | |
| box at 0, 0. | |
| Be careful when using this function, the results will not update | |
| if the artist window extent of the artist changes. The extent | |
| can change due to any changes in the transform stack, such as | |
| changing the Axes limits, the figure size, or the canvas used | |
| (as is done when saving a figure). This can lead to unexpected | |
| behavior where interactive figures will look fine on the screen, | |
| but will save incorrectly. | |
| """ | |
| return Bbox([[0, 0], [0, 0]]) | |
| def get_tightbbox(self, renderer=None): | |
| """ | |
| Like `.Artist.get_window_extent`, but includes any clipping. | |
| Parameters | |
| ---------- | |
| renderer : `~matplotlib.backend_bases.RendererBase` subclass, optional | |
| renderer that will be used to draw the figures (i.e. | |
| ``fig.canvas.get_renderer()``) | |
| Returns | |
| ------- | |
| `.Bbox` or None | |
| The enclosing bounding box (in figure pixel coordinates). | |
| Returns None if clipping results in no intersection. | |
| """ | |
| bbox = self.get_window_extent(renderer) | |
| if self.get_clip_on(): | |
| clip_box = self.get_clip_box() | |
| if clip_box is not None: | |
| bbox = Bbox.intersection(bbox, clip_box) | |
| clip_path = self.get_clip_path() | |
| if clip_path is not None and bbox is not None: | |
| clip_path = clip_path.get_fully_transformed_path() | |
| bbox = Bbox.intersection(bbox, clip_path.get_extents()) | |
| return bbox | |
| def add_callback(self, func): | |
| """ | |
| Add a callback function that will be called whenever one of the | |
| `.Artist`'s properties changes. | |
| Parameters | |
| ---------- | |
| func : callable | |
| The callback function. It must have the signature:: | |
| def func(artist: Artist) -> Any | |
| where *artist* is the calling `.Artist`. Return values may exist | |
| but are ignored. | |
| Returns | |
| ------- | |
| int | |
| The observer id associated with the callback. This id can be | |
| used for removing the callback with `.remove_callback` later. | |
| See Also | |
| -------- | |
| remove_callback | |
| """ | |
| # Wrapping func in a lambda ensures it can be connected multiple times | |
| # and never gets weakref-gc'ed. | |
| return self._callbacks.connect("pchanged", lambda: func(self)) | |
| def remove_callback(self, oid): | |
| """ | |
| Remove a callback based on its observer id. | |
| See Also | |
| -------- | |
| add_callback | |
| """ | |
| self._callbacks.disconnect(oid) | |
| def pchanged(self): | |
| """ | |
| Call all of the registered callbacks. | |
| This function is triggered internally when a property is changed. | |
| See Also | |
| -------- | |
| add_callback | |
| remove_callback | |
| """ | |
| self._callbacks.process("pchanged") | |
| def is_transform_set(self): | |
| """ | |
| Return whether the Artist has an explicitly set transform. | |
| This is *True* after `.set_transform` has been called. | |
| """ | |
| return self._transformSet | |
| def set_transform(self, t): | |
| """ | |
| Set the artist transform. | |
| Parameters | |
| ---------- | |
| t : `~matplotlib.transforms.Transform` | |
| """ | |
| self._transform = t | |
| self._transformSet = True | |
| self.pchanged() | |
| self.stale = True | |
| def get_transform(self): | |
| """Return the `.Transform` instance used by this artist.""" | |
| if self._transform is None: | |
| self._transform = IdentityTransform() | |
| elif (not isinstance(self._transform, Transform) | |
| and hasattr(self._transform, '_as_mpl_transform')): | |
| self._transform = self._transform._as_mpl_transform(self.axes) | |
| return self._transform | |
| def get_children(self): | |
| r"""Return a list of the child `.Artist`\s of this `.Artist`.""" | |
| return [] | |
| def _different_canvas(self, event): | |
| """ | |
| Check whether an *event* occurred on a canvas other that this artist's canvas. | |
| If this method returns True, the event definitely occurred on a different | |
| canvas; if it returns False, either it occurred on the same canvas, or we may | |
| not have enough information to know. | |
| Subclasses should start their definition of `contains` as follows:: | |
| if self._different_canvas(mouseevent): | |
| return False, {} | |
| # subclass-specific implementation follows | |
| """ | |
| return (getattr(event, "canvas", None) is not None | |
| and (fig := self.get_figure(root=True)) is not None | |
| and event.canvas is not fig.canvas) | |
| def contains(self, mouseevent): | |
| """ | |
| Test whether the artist contains the mouse event. | |
| Parameters | |
| ---------- | |
| mouseevent : `~matplotlib.backend_bases.MouseEvent` | |
| Returns | |
| ------- | |
| contains : bool | |
| Whether any values are within the radius. | |
| details : dict | |
| An artist-specific dictionary of details of the event context, | |
| such as which points are contained in the pick radius. See the | |
| individual Artist subclasses for details. | |
| """ | |
| _log.warning("%r needs 'contains' method", self.__class__.__name__) | |
| return False, {} | |
| def pickable(self): | |
| """ | |
| Return whether the artist is pickable. | |
| See Also | |
| -------- | |
| .Artist.set_picker, .Artist.get_picker, .Artist.pick | |
| """ | |
| return self.get_figure(root=False) is not None and self._picker is not None | |
| def pick(self, mouseevent): | |
| """ | |
| Process a pick event. | |
| Each child artist will fire a pick event if *mouseevent* is over | |
| the artist and the artist has picker set. | |
| See Also | |
| -------- | |
| .Artist.set_picker, .Artist.get_picker, .Artist.pickable | |
| """ | |
| from .backend_bases import PickEvent # Circular import. | |
| # Pick self | |
| if self.pickable(): | |
| picker = self.get_picker() | |
| if callable(picker): | |
| inside, prop = picker(self, mouseevent) | |
| else: | |
| inside, prop = self.contains(mouseevent) | |
| if inside: | |
| PickEvent("pick_event", self.get_figure(root=True).canvas, | |
| mouseevent, self, **prop)._process() | |
| # Pick children | |
| for a in self.get_children(): | |
| # make sure the event happened in the same Axes | |
| ax = getattr(a, 'axes', None) | |
| if (isinstance(a, mpl.figure.SubFigure) | |
| or mouseevent.inaxes is None or ax is None | |
| or mouseevent.inaxes == ax): | |
| # we need to check if mouseevent.inaxes is None | |
| # because some objects associated with an Axes (e.g., a | |
| # tick label) can be outside the bounding box of the | |
| # Axes and inaxes will be None | |
| # also check that ax is None so that it traverse objects | |
| # which do not have an axes property but children might | |
| a.pick(mouseevent) | |
| def set_picker(self, picker): | |
| """ | |
| Define the picking behavior of the artist. | |
| Parameters | |
| ---------- | |
| picker : None or bool or float or callable | |
| This can be one of the following: | |
| - *None*: Picking is disabled for this artist (default). | |
| - A boolean: If *True* then picking will be enabled and the | |
| artist will fire a pick event if the mouse event is over | |
| the artist. | |
| - A float: If picker is a number it is interpreted as an | |
| epsilon tolerance in points and the artist will fire | |
| off an event if its data is within epsilon of the mouse | |
| event. For some artists like lines and patch collections, | |
| the artist may provide additional data to the pick event | |
| that is generated, e.g., the indices of the data within | |
| epsilon of the pick event | |
| - A function: If picker is callable, it is a user supplied | |
| function which determines whether the artist is hit by the | |
| mouse event:: | |
| hit, props = picker(artist, mouseevent) | |
| to determine the hit test. if the mouse event is over the | |
| artist, return *hit=True* and props is a dictionary of | |
| properties you want added to the PickEvent attributes. | |
| """ | |
| self._picker = picker | |
| def get_picker(self): | |
| """ | |
| Return the picking behavior of the artist. | |
| The possible values are described in `.Artist.set_picker`. | |
| See Also | |
| -------- | |
| .Artist.set_picker, .Artist.pickable, .Artist.pick | |
| """ | |
| return self._picker | |
| def get_url(self): | |
| """Return the url.""" | |
| return self._url | |
| def set_url(self, url): | |
| """ | |
| Set the url for the artist. | |
| Parameters | |
| ---------- | |
| url : str | |
| """ | |
| self._url = url | |
| def get_gid(self): | |
| """Return the group id.""" | |
| return self._gid | |
| def set_gid(self, gid): | |
| """ | |
| Set the (group) id for the artist. | |
| Parameters | |
| ---------- | |
| gid : str | |
| """ | |
| self._gid = gid | |
| def get_snap(self): | |
| """ | |
| Return the snap setting. | |
| See `.set_snap` for details. | |
| """ | |
| if mpl.rcParams['path.snap']: | |
| return self._snap | |
| else: | |
| return False | |
| def set_snap(self, snap): | |
| """ | |
| Set the snapping behavior. | |
| Snapping aligns positions with the pixel grid, which results in | |
| clearer images. For example, if a black line of 1px width was | |
| defined at a position in between two pixels, the resulting image | |
| would contain the interpolated value of that line in the pixel grid, | |
| which would be a grey value on both adjacent pixel positions. In | |
| contrast, snapping will move the line to the nearest integer pixel | |
| value, so that the resulting image will really contain a 1px wide | |
| black line. | |
| Snapping is currently only supported by the Agg and MacOSX backends. | |
| Parameters | |
| ---------- | |
| snap : bool or None | |
| Possible values: | |
| - *True*: Snap vertices to the nearest pixel center. | |
| - *False*: Do not modify vertex positions. | |
| - *None*: (auto) If the path contains only rectilinear line | |
| segments, round to the nearest pixel center. | |
| """ | |
| self._snap = snap | |
| self.stale = True | |
| def get_sketch_params(self): | |
| """ | |
| Return the sketch parameters for the artist. | |
| Returns | |
| ------- | |
| tuple or None | |
| A 3-tuple with the following elements: | |
| - *scale*: The amplitude of the wiggle perpendicular to the | |
| source line. | |
| - *length*: The length of the wiggle along the line. | |
| - *randomness*: The scale factor by which the length is | |
| shrunken or expanded. | |
| Returns *None* if no sketch parameters were set. | |
| """ | |
| return self._sketch | |
| def set_sketch_params(self, scale=None, length=None, randomness=None): | |
| """ | |
| Set the sketch parameters. | |
| Parameters | |
| ---------- | |
| scale : float, optional | |
| The amplitude of the wiggle perpendicular to the source | |
| line, in pixels. If scale is `None`, or not provided, no | |
| sketch filter will be provided. | |
| length : float, optional | |
| The length of the wiggle along the line, in pixels | |
| (default 128.0) | |
| randomness : float, optional | |
| The scale factor by which the length is shrunken or | |
| expanded (default 16.0) | |
| The PGF backend uses this argument as an RNG seed and not as | |
| described above. Using the same seed yields the same random shape. | |
| .. ACCEPTS: (scale: float, length: float, randomness: float) | |
| """ | |
| if scale is None: | |
| self._sketch = None | |
| else: | |
| self._sketch = (scale, length or 128.0, randomness or 16.0) | |
| self.stale = True | |
| def set_path_effects(self, path_effects): | |
| """ | |
| Set the path effects. | |
| Parameters | |
| ---------- | |
| path_effects : list of `.AbstractPathEffect` | |
| """ | |
| self._path_effects = path_effects | |
| self.stale = True | |
| def get_path_effects(self): | |
| return self._path_effects | |
| def get_figure(self, root=False): | |
| """ | |
| Return the `.Figure` or `.SubFigure` instance the artist belongs to. | |
| Parameters | |
| ---------- | |
| root : bool, default=False | |
| If False, return the (Sub)Figure this artist is on. If True, | |
| return the root Figure for a nested tree of SubFigures. | |
| """ | |
| if root and self._parent_figure is not None: | |
| return self._parent_figure.get_figure(root=True) | |
| return self._parent_figure | |
| def set_figure(self, fig): | |
| """ | |
| Set the `.Figure` or `.SubFigure` instance the artist belongs to. | |
| Parameters | |
| ---------- | |
| fig : `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure` | |
| """ | |
| # if this is a no-op just return | |
| if self._parent_figure is fig: | |
| return | |
| # if we currently have a figure (the case of both `self.figure` | |
| # and *fig* being none is taken care of above) we then user is | |
| # trying to change the figure an artist is associated with which | |
| # is not allowed for the same reason as adding the same instance | |
| # to more than one Axes | |
| if self._parent_figure is not None: | |
| raise RuntimeError("Can not put single artist in " | |
| "more than one figure") | |
| self._parent_figure = fig | |
| if self._parent_figure and self._parent_figure is not self: | |
| self.pchanged() | |
| self.stale = True | |
| figure = property(get_figure, set_figure, | |
| doc=("The (Sub)Figure that the artist is on. For more " | |
| "control, use the `get_figure` method.")) | |
| def set_clip_box(self, clipbox): | |
| """ | |
| Set the artist's clip `.Bbox`. | |
| Parameters | |
| ---------- | |
| clipbox : `~matplotlib.transforms.BboxBase` or None | |
| Will typically be created from a `.TransformedBbox`. For instance, | |
| ``TransformedBbox(Bbox([[0, 0], [1, 1]]), ax.transAxes)`` is the default | |
| clipping for an artist added to an Axes. | |
| """ | |
| _api.check_isinstance((BboxBase, None), clipbox=clipbox) | |
| if clipbox != self.clipbox: | |
| self.clipbox = clipbox | |
| self.pchanged() | |
| self.stale = True | |
| def set_clip_path(self, path, transform=None): | |
| """ | |
| Set the artist's clip path. | |
| Parameters | |
| ---------- | |
| path : `~matplotlib.patches.Patch` or `.Path` or `.TransformedPath` or None | |
| The clip path. If given a `.Path`, *transform* must be provided as | |
| well. If *None*, a previously set clip path is removed. | |
| transform : `~matplotlib.transforms.Transform`, optional | |
| Only used if *path* is a `.Path`, in which case the given `.Path` | |
| is converted to a `.TransformedPath` using *transform*. | |
| Notes | |
| ----- | |
| For efficiency, if *path* is a `.Rectangle` this method will set the | |
| clipping box to the corresponding rectangle and set the clipping path | |
| to ``None``. | |
| For technical reasons (support of `~.Artist.set`), a tuple | |
| (*path*, *transform*) is also accepted as a single positional | |
| parameter. | |
| .. ACCEPTS: Patch or (Path, Transform) or None | |
| """ | |
| from matplotlib.patches import Patch, Rectangle | |
| success = False | |
| if transform is None: | |
| if isinstance(path, Rectangle): | |
| self.clipbox = TransformedBbox(Bbox.unit(), | |
| path.get_transform()) | |
| self._clippath = None | |
| success = True | |
| elif isinstance(path, Patch): | |
| self._clippath = TransformedPatchPath(path) | |
| success = True | |
| elif isinstance(path, tuple): | |
| path, transform = path | |
| if path is None: | |
| self._clippath = None | |
| success = True | |
| elif isinstance(path, Path): | |
| self._clippath = TransformedPath(path, transform) | |
| success = True | |
| elif isinstance(path, TransformedPatchPath): | |
| self._clippath = path | |
| success = True | |
| elif isinstance(path, TransformedPath): | |
| self._clippath = path | |
| success = True | |
| if not success: | |
| raise TypeError( | |
| "Invalid arguments to set_clip_path, of type " | |
| f"{type(path).__name__} and {type(transform).__name__}") | |
| # This may result in the callbacks being hit twice, but guarantees they | |
| # will be hit at least once. | |
| self.pchanged() | |
| self.stale = True | |
| def get_alpha(self): | |
| """ | |
| Return the alpha value used for blending - not supported on all | |
| backends. | |
| """ | |
| return self._alpha | |
| def get_visible(self): | |
| """Return the visibility.""" | |
| return self._visible | |
| def get_animated(self): | |
| """Return whether the artist is animated.""" | |
| return self._animated | |
| def get_in_layout(self): | |
| """ | |
| Return boolean flag, ``True`` if artist is included in layout | |
| calculations. | |
| E.g. :ref:`constrainedlayout_guide`, | |
| `.Figure.tight_layout()`, and | |
| ``fig.savefig(fname, bbox_inches='tight')``. | |
| """ | |
| return self._in_layout | |
| def _fully_clipped_to_axes(self): | |
| """ | |
| Return a boolean flag, ``True`` if the artist is clipped to the Axes | |
| and can thus be skipped in layout calculations. Requires `get_clip_on` | |
| is True, one of `clip_box` or `clip_path` is set, ``clip_box.extents`` | |
| is equivalent to ``ax.bbox.extents`` (if set), and ``clip_path._patch`` | |
| is equivalent to ``ax.patch`` (if set). | |
| """ | |
| # Note that ``clip_path.get_fully_transformed_path().get_extents()`` | |
| # cannot be directly compared to ``axes.bbox.extents`` because the | |
| # extents may be undefined (i.e. equivalent to ``Bbox.null()``) | |
| # before the associated artist is drawn, and this method is meant | |
| # to determine whether ``axes.get_tightbbox()`` may bypass drawing | |
| clip_box = self.get_clip_box() | |
| clip_path = self.get_clip_path() | |
| return (self.axes is not None | |
| and self.get_clip_on() | |
| and (clip_box is not None or clip_path is not None) | |
| and (clip_box is None | |
| or np.all(clip_box.extents == self.axes.bbox.extents)) | |
| and (clip_path is None | |
| or isinstance(clip_path, TransformedPatchPath) | |
| and clip_path._patch is self.axes.patch)) | |
| def get_clip_on(self): | |
| """Return whether the artist uses clipping.""" | |
| return self._clipon | |
| def get_clip_box(self): | |
| """Return the clipbox.""" | |
| return self.clipbox | |
| def get_clip_path(self): | |
| """Return the clip path.""" | |
| return self._clippath | |
| def get_transformed_clip_path_and_affine(self): | |
| """ | |
| Return the clip path with the non-affine part of its | |
| transformation applied, and the remaining affine part of its | |
| transformation. | |
| """ | |
| if self._clippath is not None: | |
| return self._clippath.get_transformed_path_and_affine() | |
| return None, None | |
| def set_clip_on(self, b): | |
| """ | |
| Set whether the artist uses clipping. | |
| When False, artists will be visible outside the Axes which | |
| can lead to unexpected results. | |
| Parameters | |
| ---------- | |
| b : bool | |
| """ | |
| self._clipon = b | |
| # This may result in the callbacks being hit twice, but ensures they | |
| # are hit at least once | |
| self.pchanged() | |
| self.stale = True | |
| def _set_gc_clip(self, gc): | |
| """Set the clip properly for the gc.""" | |
| if self._clipon: | |
| if self.clipbox is not None: | |
| gc.set_clip_rectangle(self.clipbox) | |
| gc.set_clip_path(self._clippath) | |
| else: | |
| gc.set_clip_rectangle(None) | |
| gc.set_clip_path(None) | |
| def get_rasterized(self): | |
| """Return whether the artist is to be rasterized.""" | |
| return self._rasterized | |
| def set_rasterized(self, rasterized): | |
| """ | |
| Force rasterized (bitmap) drawing for vector graphics output. | |
| Rasterized drawing is not supported by all artists. If you try to | |
| enable this on an artist that does not support it, the command has no | |
| effect and a warning will be issued. | |
| This setting is ignored for pixel-based output. | |
| See also :doc:`/gallery/misc/rasterization_demo`. | |
| Parameters | |
| ---------- | |
| rasterized : bool | |
| """ | |
| supports_rasterization = getattr(self.draw, | |
| "_supports_rasterization", False) | |
| if rasterized and not supports_rasterization: | |
| _api.warn_external(f"Rasterization of '{self}' will be ignored") | |
| self._rasterized = rasterized | |
| def get_agg_filter(self): | |
| """Return filter function to be used for agg filter.""" | |
| return self._agg_filter | |
| def set_agg_filter(self, filter_func): | |
| """ | |
| Set the agg filter. | |
| Parameters | |
| ---------- | |
| filter_func : callable | |
| A filter function, which takes a (m, n, depth) float array | |
| and a dpi value, and returns a (m, n, depth) array and two | |
| offsets from the bottom left corner of the image | |
| .. ACCEPTS: a filter function, which takes a (m, n, 3) float array | |
| and a dpi value, and returns a (m, n, 3) array and two offsets | |
| from the bottom left corner of the image | |
| """ | |
| self._agg_filter = filter_func | |
| self.stale = True | |
| def draw(self, renderer): | |
| """ | |
| Draw the Artist (and its children) using the given renderer. | |
| This has no effect if the artist is not visible (`.Artist.get_visible` | |
| returns False). | |
| Parameters | |
| ---------- | |
| renderer : `~matplotlib.backend_bases.RendererBase` subclass. | |
| Notes | |
| ----- | |
| This method is overridden in the Artist subclasses. | |
| """ | |
| if not self.get_visible(): | |
| return | |
| self.stale = False | |
| def set_alpha(self, alpha): | |
| """ | |
| Set the alpha value used for blending - not supported on all backends. | |
| Parameters | |
| ---------- | |
| alpha : float or None | |
| *alpha* must be within the 0-1 range, inclusive. | |
| """ | |
| if alpha is not None and not isinstance(alpha, Real): | |
| raise TypeError( | |
| f'alpha must be numeric or None, not {type(alpha)}') | |
| if alpha is not None and not (0 <= alpha <= 1): | |
| raise ValueError(f'alpha ({alpha}) is outside 0-1 range') | |
| if alpha != self._alpha: | |
| self._alpha = alpha | |
| self.pchanged() | |
| self.stale = True | |
| def _set_alpha_for_array(self, alpha): | |
| """ | |
| Set the alpha value used for blending - not supported on all backends. | |
| Parameters | |
| ---------- | |
| alpha : array-like or float or None | |
| All values must be within the 0-1 range, inclusive. | |
| Masked values and nans are not supported. | |
| """ | |
| if isinstance(alpha, str): | |
| raise TypeError("alpha must be numeric or None, not a string") | |
| if not np.iterable(alpha): | |
| Artist.set_alpha(self, alpha) | |
| return | |
| alpha = np.asarray(alpha) | |
| if not (0 <= alpha.min() and alpha.max() <= 1): | |
| raise ValueError('alpha must be between 0 and 1, inclusive, ' | |
| f'but min is {alpha.min()}, max is {alpha.max()}') | |
| self._alpha = alpha | |
| self.pchanged() | |
| self.stale = True | |
| def set_visible(self, b): | |
| """ | |
| Set the artist's visibility. | |
| Parameters | |
| ---------- | |
| b : bool | |
| """ | |
| if b != self._visible: | |
| self._visible = b | |
| self.pchanged() | |
| self.stale = True | |
| def set_animated(self, b): | |
| """ | |
| Set whether the artist is intended to be used in an animation. | |
| If True, the artist is excluded from regular drawing of the figure. | |
| You have to call `.Figure.draw_artist` / `.Axes.draw_artist` | |
| explicitly on the artist. This approach is used to speed up animations | |
| using blitting. | |
| See also `matplotlib.animation` and | |
| :ref:`blitting`. | |
| Parameters | |
| ---------- | |
| b : bool | |
| """ | |
| if self._animated != b: | |
| self._animated = b | |
| self.pchanged() | |
| def set_in_layout(self, in_layout): | |
| """ | |
| Set if artist is to be included in layout calculations, | |
| E.g. :ref:`constrainedlayout_guide`, | |
| `.Figure.tight_layout()`, and | |
| ``fig.savefig(fname, bbox_inches='tight')``. | |
| Parameters | |
| ---------- | |
| in_layout : bool | |
| """ | |
| self._in_layout = in_layout | |
| def get_label(self): | |
| """Return the label used for this artist in the legend.""" | |
| return self._label | |
| def set_label(self, s): | |
| """ | |
| Set a label that will be displayed in the legend. | |
| Parameters | |
| ---------- | |
| s : object | |
| *s* will be converted to a string by calling `str`. | |
| """ | |
| label = str(s) if s is not None else None | |
| if label != self._label: | |
| self._label = label | |
| self.pchanged() | |
| self.stale = True | |
| def get_zorder(self): | |
| """Return the artist's zorder.""" | |
| return self.zorder | |
| def set_zorder(self, level): | |
| """ | |
| Set the zorder for the artist. Artists with lower zorder | |
| values are drawn first. | |
| Parameters | |
| ---------- | |
| level : float | |
| """ | |
| if level is None: | |
| level = self.__class__.zorder | |
| if level != self.zorder: | |
| self.zorder = level | |
| self.pchanged() | |
| self.stale = True | |
| def sticky_edges(self): | |
| """ | |
| ``x`` and ``y`` sticky edge lists for autoscaling. | |
| When performing autoscaling, if a data limit coincides with a value in | |
| the corresponding sticky_edges list, then no margin will be added--the | |
| view limit "sticks" to the edge. A typical use case is histograms, | |
| where one usually expects no margin on the bottom edge (0) of the | |
| histogram. | |
| Moreover, margin expansion "bumps" against sticky edges and cannot | |
| cross them. For example, if the upper data limit is 1.0, the upper | |
| view limit computed by simple margin application is 1.2, but there is a | |
| sticky edge at 1.1, then the actual upper view limit will be 1.1. | |
| This attribute cannot be assigned to; however, the ``x`` and ``y`` | |
| lists can be modified in place as needed. | |
| Examples | |
| -------- | |
| >>> artist.sticky_edges.x[:] = (xmin, xmax) | |
| >>> artist.sticky_edges.y[:] = (ymin, ymax) | |
| """ | |
| return self._sticky_edges | |
| def update_from(self, other): | |
| """Copy properties from *other* to *self*.""" | |
| self._transform = other._transform | |
| self._transformSet = other._transformSet | |
| self._visible = other._visible | |
| self._alpha = other._alpha | |
| self.clipbox = other.clipbox | |
| self._clipon = other._clipon | |
| self._clippath = other._clippath | |
| self._label = other._label | |
| self._sketch = other._sketch | |
| self._path_effects = other._path_effects | |
| self.sticky_edges.x[:] = other.sticky_edges.x.copy() | |
| self.sticky_edges.y[:] = other.sticky_edges.y.copy() | |
| self.pchanged() | |
| self.stale = True | |
| def properties(self): | |
| """Return a dictionary of all the properties of the artist.""" | |
| return ArtistInspector(self).properties() | |
| def _update_props(self, props, errfmt): | |
| """ | |
| Helper for `.Artist.set` and `.Artist.update`. | |
| *errfmt* is used to generate error messages for invalid property | |
| names; it gets formatted with ``type(self)`` for "{cls}" and the | |
| property name for "{prop_name}". | |
| """ | |
| ret = [] | |
| with cbook._setattr_cm(self, eventson=False): | |
| for k, v in props.items(): | |
| # Allow attributes we want to be able to update through | |
| # art.update, art.set, setp. | |
| if k == "axes": | |
| ret.append(setattr(self, k, v)) | |
| else: | |
| func = getattr(self, f"set_{k}", None) | |
| if not callable(func): | |
| raise AttributeError( | |
| errfmt.format(cls=type(self), prop_name=k), | |
| name=k) | |
| ret.append(func(v)) | |
| if ret: | |
| self.pchanged() | |
| self.stale = True | |
| return ret | |
| def update(self, props): | |
| """ | |
| Update this artist's properties from the dict *props*. | |
| Parameters | |
| ---------- | |
| props : dict | |
| """ | |
| return self._update_props( | |
| props, "{cls.__name__!r} object has no property {prop_name!r}") | |
| def _internal_update(self, kwargs): | |
| """ | |
| Update artist properties without prenormalizing them, but generating | |
| errors as if calling `set`. | |
| The lack of prenormalization is to maintain backcompatibility. | |
| """ | |
| return self._update_props( | |
| kwargs, "{cls.__name__}.set() got an unexpected keyword argument " | |
| "{prop_name!r}") | |
| def set(self, **kwargs): | |
| # docstring and signature are auto-generated via | |
| # Artist._update_set_signature_and_docstring() at the end of the | |
| # module. | |
| return self._internal_update(cbook.normalize_kwargs(kwargs, self)) | |
| def _cm_set(self, **kwargs): | |
| """ | |
| `.Artist.set` context-manager that restores original values at exit. | |
| """ | |
| orig_vals = {k: getattr(self, f"get_{k}")() for k in kwargs} | |
| try: | |
| self.set(**kwargs) | |
| yield | |
| finally: | |
| self.set(**orig_vals) | |
| def findobj(self, match=None, include_self=True): | |
| """ | |
| Find artist objects. | |
| Recursively find all `.Artist` instances contained in the artist. | |
| Parameters | |
| ---------- | |
| match | |
| A filter criterion for the matches. This can be | |
| - *None*: Return all objects contained in artist. | |
| - A function with signature ``def match(artist: Artist) -> bool``. | |
| The result will only contain artists for which the function | |
| returns *True*. | |
| - A class instance: e.g., `.Line2D`. The result will only contain | |
| artists of this class or its subclasses (``isinstance`` check). | |
| include_self : bool | |
| Include *self* in the list to be checked for a match. | |
| Returns | |
| ------- | |
| list of `.Artist` | |
| """ | |
| if match is None: # always return True | |
| def matchfunc(x): | |
| return True | |
| elif isinstance(match, type) and issubclass(match, Artist): | |
| def matchfunc(x): | |
| return isinstance(x, match) | |
| elif callable(match): | |
| matchfunc = match | |
| else: | |
| raise ValueError('match must be None, a matplotlib.artist.Artist ' | |
| 'subclass, or a callable') | |
| artists = reduce(operator.iadd, | |
| [c.findobj(matchfunc) for c in self.get_children()], []) | |
| if include_self and matchfunc(self): | |
| artists.append(self) | |
| return artists | |
| def get_cursor_data(self, event): | |
| """ | |
| Return the cursor data for a given event. | |
| .. note:: | |
| This method is intended to be overridden by artist subclasses. | |
| As an end-user of Matplotlib you will most likely not call this | |
| method yourself. | |
| Cursor data can be used by Artists to provide additional context | |
| information for a given event. The default implementation just returns | |
| *None*. | |
| Subclasses can override the method and return arbitrary data. However, | |
| when doing so, they must ensure that `.format_cursor_data` can convert | |
| the data to a string representation. | |
| The only current use case is displaying the z-value of an `.AxesImage` | |
| in the status bar of a plot window, while moving the mouse. | |
| Parameters | |
| ---------- | |
| event : `~matplotlib.backend_bases.MouseEvent` | |
| See Also | |
| -------- | |
| format_cursor_data | |
| """ | |
| return None | |
| def format_cursor_data(self, data): | |
| """ | |
| Return a string representation of *data*. | |
| .. note:: | |
| This method is intended to be overridden by artist subclasses. | |
| As an end-user of Matplotlib you will most likely not call this | |
| method yourself. | |
| The default implementation converts ints and floats and arrays of ints | |
| and floats into a comma-separated string enclosed in square brackets, | |
| unless the artist has an associated colorbar, in which case scalar | |
| values are formatted using the colorbar's formatter. | |
| See Also | |
| -------- | |
| get_cursor_data | |
| """ | |
| if np.ndim(data) == 0 and hasattr(self, "_format_cursor_data_override"): | |
| # workaround for ScalarMappable to be able to define its own | |
| # format_cursor_data(). See ScalarMappable._format_cursor_data_override | |
| # for details. | |
| return self._format_cursor_data_override(data) | |
| else: | |
| try: | |
| data[0] | |
| except (TypeError, IndexError): | |
| data = [data] | |
| data_str = ', '.join(f'{item:0.3g}' for item in data | |
| if isinstance(item, Number)) | |
| return "[" + data_str + "]" | |
| def get_mouseover(self): | |
| """ | |
| Return whether this artist is queried for custom context information | |
| when the mouse cursor moves over it. | |
| """ | |
| return self._mouseover | |
| def set_mouseover(self, mouseover): | |
| """ | |
| Set whether this artist is queried for custom context information when | |
| the mouse cursor moves over it. | |
| Parameters | |
| ---------- | |
| mouseover : bool | |
| See Also | |
| -------- | |
| get_cursor_data | |
| .ToolCursorPosition | |
| .NavigationToolbar2 | |
| """ | |
| self._mouseover = bool(mouseover) | |
| ax = self.axes | |
| if ax: | |
| if self._mouseover: | |
| ax._mouseover_set.add(self) | |
| else: | |
| ax._mouseover_set.discard(self) | |
| mouseover = property(get_mouseover, set_mouseover) # backcompat. | |
| def _get_tightbbox_for_layout_only(obj, *args, **kwargs): | |
| """ | |
| Matplotlib's `.Axes.get_tightbbox` and `.Axis.get_tightbbox` support a | |
| *for_layout_only* kwarg; this helper tries to use the kwarg but skips it | |
| when encountering third-party subclasses that do not support it. | |
| """ | |
| try: | |
| return obj.get_tightbbox(*args, **{**kwargs, "for_layout_only": True}) | |
| except TypeError: | |
| return obj.get_tightbbox(*args, **kwargs) | |
| class ArtistInspector: | |
| """ | |
| A helper class to inspect an `~matplotlib.artist.Artist` and return | |
| information about its settable properties and their current values. | |
| """ | |
| def __init__(self, o): | |
| r""" | |
| Initialize the artist inspector with an `Artist` or an iterable of | |
| `Artist`\s. If an iterable is used, we assume it is a homogeneous | |
| sequence (all `Artist`\s are of the same type) and it is your | |
| responsibility to make sure this is so. | |
| """ | |
| if not isinstance(o, Artist): | |
| if np.iterable(o): | |
| o = list(o) | |
| if len(o): | |
| o = o[0] | |
| self.oorig = o | |
| if not isinstance(o, type): | |
| o = type(o) | |
| self.o = o | |
| self.aliasd = self.get_aliases() | |
| def get_aliases(self): | |
| """ | |
| Get a dict mapping property fullnames to sets of aliases for each alias | |
| in the :class:`~matplotlib.artist.ArtistInspector`. | |
| e.g., for lines:: | |
| {'markerfacecolor': {'mfc'}, | |
| 'linewidth' : {'lw'}, | |
| } | |
| """ | |
| names = [name for name in dir(self.o) | |
| if name.startswith(('set_', 'get_')) | |
| and callable(getattr(self.o, name))] | |
| aliases = {} | |
| for name in names: | |
| func = getattr(self.o, name) | |
| if not self.is_alias(func): | |
| continue | |
| propname = re.search(f"`({name[:4]}.*)`", # get_.*/set_.* | |
| inspect.getdoc(func)).group(1) | |
| aliases.setdefault(propname[4:], set()).add(name[4:]) | |
| return aliases | |
| _get_valid_values_regex = re.compile( | |
| r"\n\s*(?:\.\.\s+)?ACCEPTS:\s*((?:.|\n)*?)(?:$|(?:\n\n))" | |
| ) | |
| def get_valid_values(self, attr): | |
| """ | |
| Get the legal arguments for the setter associated with *attr*. | |
| This is done by querying the docstring of the setter for a line that | |
| begins with "ACCEPTS:" or ".. ACCEPTS:", and then by looking for a | |
| numpydoc-style documentation for the setter's first argument. | |
| """ | |
| name = 'set_%s' % attr | |
| if not hasattr(self.o, name): | |
| raise AttributeError(f'{self.o} has no function {name}') | |
| func = getattr(self.o, name) | |
| if hasattr(func, '_kwarg_doc'): | |
| return func._kwarg_doc | |
| docstring = inspect.getdoc(func) | |
| if docstring is None: | |
| return 'unknown' | |
| if docstring.startswith('Alias for '): | |
| return None | |
| match = self._get_valid_values_regex.search(docstring) | |
| if match is not None: | |
| return re.sub("\n *", " ", match.group(1)) | |
| # Much faster than list(inspect.signature(func).parameters)[1], | |
| # although barely relevant wrt. matplotlib's total import time. | |
| param_name = func.__code__.co_varnames[1] | |
| # We could set the presence * based on whether the parameter is a | |
| # varargs (it can't be a varkwargs) but it's not really worth it. | |
| match = re.search(fr"(?m)^ *\*?{param_name} : (.+)", docstring) | |
| if match: | |
| return match.group(1) | |
| return 'unknown' | |
| def _replace_path(self, source_class): | |
| """ | |
| Changes the full path to the public API path that is used | |
| in sphinx. This is needed for links to work. | |
| """ | |
| replace_dict = {'_base._AxesBase': 'Axes', | |
| '_axes.Axes': 'Axes'} | |
| for key, value in replace_dict.items(): | |
| source_class = source_class.replace(key, value) | |
| return source_class | |
| def get_setters(self): | |
| """ | |
| Get the attribute strings with setters for object. | |
| For example, for a line, return ``['markerfacecolor', 'linewidth', | |
| ....]``. | |
| """ | |
| setters = [] | |
| for name in dir(self.o): | |
| if not name.startswith('set_'): | |
| continue | |
| func = getattr(self.o, name) | |
| if (not callable(func) | |
| or self.number_of_parameters(func) < 2 | |
| or self.is_alias(func)): | |
| continue | |
| setters.append(name[4:]) | |
| return setters | |
| def number_of_parameters(func): | |
| """Return number of parameters of the callable *func*.""" | |
| return len(inspect.signature(func).parameters) | |
| def is_alias(method): | |
| """ | |
| Return whether the object *method* is an alias for another method. | |
| """ | |
| ds = inspect.getdoc(method) | |
| if ds is None: | |
| return False | |
| return ds.startswith('Alias for ') | |
| def aliased_name(self, s): | |
| """ | |
| Return 'PROPNAME or alias' if *s* has an alias, else return 'PROPNAME'. | |
| For example, for the line markerfacecolor property, which has an | |
| alias, return 'markerfacecolor or mfc' and for the transform | |
| property, which does not, return 'transform'. | |
| """ | |
| aliases = ''.join(' or %s' % x for x in sorted(self.aliasd.get(s, []))) | |
| return s + aliases | |
| _NOT_LINKABLE = { | |
| # A set of property setter methods that are not available in our | |
| # current docs. This is a workaround used to prevent trying to link | |
| # these setters which would lead to "target reference not found" | |
| # warnings during doc build. | |
| 'matplotlib.image._ImageBase.set_alpha', | |
| 'matplotlib.image._ImageBase.set_array', | |
| 'matplotlib.image._ImageBase.set_data', | |
| 'matplotlib.image._ImageBase.set_filternorm', | |
| 'matplotlib.image._ImageBase.set_filterrad', | |
| 'matplotlib.image._ImageBase.set_interpolation', | |
| 'matplotlib.image._ImageBase.set_interpolation_stage', | |
| 'matplotlib.image._ImageBase.set_resample', | |
| 'matplotlib.text._AnnotationBase.set_annotation_clip', | |
| } | |
| def aliased_name_rest(self, s, target): | |
| """ | |
| Return 'PROPNAME or alias' if *s* has an alias, else return 'PROPNAME', | |
| formatted for reST. | |
| For example, for the line markerfacecolor property, which has an | |
| alias, return 'markerfacecolor or mfc' and for the transform | |
| property, which does not, return 'transform'. | |
| """ | |
| # workaround to prevent "reference target not found" | |
| if target in self._NOT_LINKABLE: | |
| return f'``{s}``' | |
| aliases = ''.join( | |
| f' or :meth:`{a} <{target}>`' for a in sorted(self.aliasd.get(s, []))) | |
| return f':meth:`{s} <{target}>`{aliases}' | |
| def pprint_setters(self, prop=None, leadingspace=2): | |
| """ | |
| If *prop* is *None*, return a list of strings of all settable | |
| properties and their valid values. | |
| If *prop* is not *None*, it is a valid property name and that | |
| property will be returned as a string of property : valid | |
| values. | |
| """ | |
| if leadingspace: | |
| pad = ' ' * leadingspace | |
| else: | |
| pad = '' | |
| if prop is not None: | |
| accepts = self.get_valid_values(prop) | |
| return f'{pad}{prop}: {accepts}' | |
| lines = [] | |
| for prop in sorted(self.get_setters()): | |
| accepts = self.get_valid_values(prop) | |
| name = self.aliased_name(prop) | |
| lines.append(f'{pad}{name}: {accepts}') | |
| return lines | |
| def pprint_setters_rest(self, prop=None, leadingspace=4): | |
| """ | |
| If *prop* is *None*, return a list of reST-formatted strings of all | |
| settable properties and their valid values. | |
| If *prop* is not *None*, it is a valid property name and that | |
| property will be returned as a string of "property : valid" | |
| values. | |
| """ | |
| if leadingspace: | |
| pad = ' ' * leadingspace | |
| else: | |
| pad = '' | |
| if prop is not None: | |
| accepts = self.get_valid_values(prop) | |
| return f'{pad}{prop}: {accepts}' | |
| prop_and_qualnames = [] | |
| for prop in sorted(self.get_setters()): | |
| # Find the parent method which actually provides the docstring. | |
| for cls in self.o.__mro__: | |
| method = getattr(cls, f"set_{prop}", None) | |
| if method and method.__doc__ is not None: | |
| break | |
| else: # No docstring available. | |
| method = getattr(self.o, f"set_{prop}") | |
| prop_and_qualnames.append( | |
| (prop, f"{method.__module__}.{method.__qualname__}")) | |
| names = [self.aliased_name_rest(prop, target) | |
| .replace('_base._AxesBase', 'Axes') | |
| .replace('_axes.Axes', 'Axes') | |
| for prop, target in prop_and_qualnames] | |
| accepts = [self.get_valid_values(prop) | |
| for prop, _ in prop_and_qualnames] | |
| col0_len = max(len(n) for n in names) | |
| col1_len = max(len(a) for a in accepts) | |
| table_formatstr = pad + ' ' + '=' * col0_len + ' ' + '=' * col1_len | |
| return [ | |
| '', | |
| pad + '.. table::', | |
| pad + ' :class: property-table', | |
| '', | |
| table_formatstr, | |
| pad + ' ' + 'Property'.ljust(col0_len) | |
| + ' ' + 'Description'.ljust(col1_len), | |
| table_formatstr, | |
| *[pad + ' ' + n.ljust(col0_len) + ' ' + a.ljust(col1_len) | |
| for n, a in zip(names, accepts)], | |
| table_formatstr, | |
| '', | |
| ] | |
| def properties(self): | |
| """Return a dictionary mapping property name -> value.""" | |
| o = self.oorig | |
| getters = [name for name in dir(o) | |
| if name.startswith('get_') and callable(getattr(o, name))] | |
| getters.sort() | |
| d = {} | |
| for name in getters: | |
| func = getattr(o, name) | |
| if self.is_alias(func): | |
| continue | |
| try: | |
| with warnings.catch_warnings(): | |
| warnings.simplefilter('ignore') | |
| val = func() | |
| except Exception: | |
| continue | |
| else: | |
| d[name[4:]] = val | |
| return d | |
| def pprint_getters(self): | |
| """Return the getters and actual values as list of strings.""" | |
| lines = [] | |
| for name, val in sorted(self.properties().items()): | |
| if getattr(val, 'shape', ()) != () and len(val) > 6: | |
| s = str(val[:6]) + '...' | |
| else: | |
| s = str(val) | |
| s = s.replace('\n', ' ') | |
| if len(s) > 50: | |
| s = s[:50] + '...' | |
| name = self.aliased_name(name) | |
| lines.append(f' {name} = {s}') | |
| return lines | |
| def getp(obj, property=None): | |
| """ | |
| Return the value of an `.Artist`'s *property*, or print all of them. | |
| Parameters | |
| ---------- | |
| obj : `~matplotlib.artist.Artist` | |
| The queried artist; e.g., a `.Line2D`, a `.Text`, or an `~.axes.Axes`. | |
| property : str or None, default: None | |
| If *property* is 'somename', this function returns | |
| ``obj.get_somename()``. | |
| If it's None (or unset), it *prints* all gettable properties from | |
| *obj*. Many properties have aliases for shorter typing, e.g. 'lw' is | |
| an alias for 'linewidth'. In the output, aliases and full property | |
| names will be listed as: | |
| property or alias = value | |
| e.g.: | |
| linewidth or lw = 2 | |
| See Also | |
| -------- | |
| setp | |
| """ | |
| if property is None: | |
| insp = ArtistInspector(obj) | |
| ret = insp.pprint_getters() | |
| print('\n'.join(ret)) | |
| return | |
| return getattr(obj, 'get_' + property)() | |
| # alias | |
| get = getp | |
| def setp(obj, *args, file=None, **kwargs): | |
| """ | |
| Set one or more properties on an `.Artist`, or list allowed values. | |
| Parameters | |
| ---------- | |
| obj : `~matplotlib.artist.Artist` or list of `.Artist` | |
| The artist(s) whose properties are being set or queried. When setting | |
| properties, all artists are affected; when querying the allowed values, | |
| only the first instance in the sequence is queried. | |
| For example, two lines can be made thicker and red with a single call: | |
| >>> x = arange(0, 1, 0.01) | |
| >>> lines = plot(x, sin(2*pi*x), x, sin(4*pi*x)) | |
| >>> setp(lines, linewidth=2, color='r') | |
| file : file-like, default: `sys.stdout` | |
| Where `setp` writes its output when asked to list allowed values. | |
| >>> with open('output.log') as file: | |
| ... setp(line, file=file) | |
| The default, ``None``, means `sys.stdout`. | |
| *args, **kwargs | |
| The properties to set. The following combinations are supported: | |
| - Set the linestyle of a line to be dashed: | |
| >>> line, = plot([1, 2, 3]) | |
| >>> setp(line, linestyle='--') | |
| - Set multiple properties at once: | |
| >>> setp(line, linewidth=2, color='r') | |
| - List allowed values for a line's linestyle: | |
| >>> setp(line, 'linestyle') | |
| linestyle: {'-', '--', '-.', ':', '', (offset, on-off-seq), ...} | |
| - List all properties that can be set, and their allowed values: | |
| >>> setp(line) | |
| agg_filter: a filter function, ... | |
| [long output listing omitted] | |
| `setp` also supports MATLAB style string/value pairs. For example, the | |
| following are equivalent: | |
| >>> setp(lines, 'linewidth', 2, 'color', 'r') # MATLAB style | |
| >>> setp(lines, linewidth=2, color='r') # Python style | |
| See Also | |
| -------- | |
| getp | |
| """ | |
| if isinstance(obj, Artist): | |
| objs = [obj] | |
| else: | |
| objs = list(cbook.flatten(obj)) | |
| if not objs: | |
| return | |
| insp = ArtistInspector(objs[0]) | |
| if not kwargs and len(args) < 2: | |
| if args: | |
| print(insp.pprint_setters(prop=args[0]), file=file) | |
| else: | |
| print('\n'.join(insp.pprint_setters()), file=file) | |
| return | |
| if len(args) % 2: | |
| raise ValueError('The set args must be string, value pairs') | |
| funcvals = dict(zip(args[::2], args[1::2])) | |
| ret = [o.update(funcvals) for o in objs] + [o.set(**kwargs) for o in objs] | |
| return list(cbook.flatten(ret)) | |
| def kwdoc(artist): | |
| r""" | |
| Inspect an `~matplotlib.artist.Artist` class (using `.ArtistInspector`) and | |
| return information about its settable properties and their current values. | |
| Parameters | |
| ---------- | |
| artist : `~matplotlib.artist.Artist` or an iterable of `Artist`\s | |
| Returns | |
| ------- | |
| str | |
| The settable properties of *artist*, as plain text if | |
| :rc:`docstring.hardcopy` is False and as a rst table (intended for | |
| use in Sphinx) if it is True. | |
| """ | |
| ai = ArtistInspector(artist) | |
| return ('\n'.join(ai.pprint_setters_rest(leadingspace=4)) | |
| if mpl.rcParams['docstring.hardcopy'] else | |
| 'Properties:\n' + '\n'.join(ai.pprint_setters(leadingspace=4))) | |
| # We defer this to the end of them module, because it needs ArtistInspector | |
| # to be defined. | |
| Artist._update_set_signature_and_docstring() | |
Xet Storage Details
- Size:
- 63.3 kB
- Xet hash:
- 34dace85a2d20ce098e192b50aa707ed26374b359c0025ff323a23fc4a452ab9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.