from collections import OrderedDict, namedtuple
|
|
from functools import wraps
|
|
import inspect
|
|
import re
|
|
import warnings
|
|
|
|
import numpy as np
|
|
|
|
import matplotlib
|
|
from . import cbook, docstring, rcParams
|
|
from .path import Path
|
|
from .transforms import (Bbox, IdentityTransform, Transform, TransformedBbox,
|
|
TransformedPatchPath, TransformedPath)
|
|
# Note, matplotlib artists use the doc strings for set and get
|
|
# methods to enable the introspection methods of setp and getp. Every
|
|
# set_* method should have a docstring containing the line
|
|
#
|
|
# ACCEPTS: [ legal | values ]
|
|
#
|
|
# and aliases for setters and getters should have a docstring that
|
|
# starts with 'alias for ', as in 'alias for set_somemethod'
|
|
#
|
|
# You may wonder why we use so much boiler-plate manually defining the
|
|
# set_alias and get_alias functions, rather than using some clever
|
|
# python trick. The answer is that I need to be able to manipulate
|
|
# the docstring, and there is no clever way to do that in python 2.2,
|
|
# as far as I can see - see
|
|
#
|
|
# https://mail.python.org/pipermail/python-list/2004-October/242925.html
|
|
|
|
|
|
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.
|
|
"""
|
|
|
|
# the axes class has a second argument inframe for its draw method.
|
|
@wraps(draw)
|
|
def draw_wrapper(artist, renderer, *args, **kwargs):
|
|
try:
|
|
if artist.get_rasterized():
|
|
renderer.start_rasterizing()
|
|
if artist.get_agg_filter() is not None:
|
|
renderer.start_filter()
|
|
|
|
return draw(artist, renderer, *args, **kwargs)
|
|
finally:
|
|
if artist.get_agg_filter() is not None:
|
|
renderer.stop_filter(artist.get_agg_filter())
|
|
if artist.get_rasterized():
|
|
renderer.stop_rasterizing()
|
|
|
|
draw_wrapper._supports_rasterization = True
|
|
return draw_wrapper
|
|
|
|
|
|
def _stale_axes_callback(self, val):
|
|
if self.axes:
|
|
self.axes.stale = val
|
|
|
|
|
|
_XYPair = namedtuple("_XYPair", "x y")
|
|
|
|
|
|
class Artist(object):
|
|
"""
|
|
Abstract base class for someone who renders into a
|
|
:class:`FigureCanvas`.
|
|
"""
|
|
|
|
aname = 'Artist'
|
|
zorder = 0
|
|
# order of precedence when bulk setting/updating properties
|
|
# via update. The keys should be property names and the values
|
|
# integers
|
|
_prop_order = dict(color=-1)
|
|
|
|
def __init__(self):
|
|
self._stale = True
|
|
self.stale_callback = None
|
|
self._axes = None
|
|
self.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._contains = None
|
|
self._rasterized = None
|
|
self._agg_filter = None
|
|
self._mouseover = False
|
|
self.eventson = False # fire events only if eventson
|
|
self._oid = 0 # an observer id
|
|
self._propobservers = {} # a dict from oids to funcs
|
|
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 = rcParams['path.sketch']
|
|
self._path_effects = rcParams['path.effects']
|
|
self._sticky_edges = _XYPair([], [])
|
|
self._in_layout = True
|
|
|
|
def __getstate__(self):
|
|
d = self.__dict__.copy()
|
|
# remove the unpicklable remove method, this will get re-added on load
|
|
# (by the axes) if the artist lives on an axes.
|
|
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
|
|
:meth:`matplotlib.axes.Axes.draw_idle`. Call
|
|
:meth:`matplotlib.axes.Axes.relim` to update the axes limits
|
|
if desired.
|
|
|
|
Note: :meth:`~matplotlib.axes.Axes.relim` will not see
|
|
collections even if the collection was added to 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)
|
|
# mark the axes as stale
|
|
self.axes.stale = True
|
|
# decouple the artist from the axes
|
|
self.axes = None
|
|
_ax_flag = True
|
|
|
|
if self.figure:
|
|
self.figure = None
|
|
if not _ax_flag:
|
|
self.figure = True
|
|
|
|
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 *True* if units are set on the *x* or *y* axes'
|
|
ax = self.axes
|
|
if ax is None or ax.xaxis is None:
|
|
return False
|
|
return ax.xaxis.have_units() or ax.yaxis.have_units()
|
|
|
|
def convert_xunits(self, x):
|
|
"""For artists in an axes, if the xaxis has units support,
|
|
convert *x* using xaxis unit type
|
|
"""
|
|
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):
|
|
"""For artists in an axes, if the yaxis has units support,
|
|
convert *y* using yaxis unit type
|
|
"""
|
|
ax = getattr(self, 'axes', None)
|
|
if ax is None or ax.yaxis is None:
|
|
return y
|
|
return ax.yaxis.convert_units(y)
|
|
|
|
@property
|
|
def axes(self):
|
|
"""
|
|
The :class:`~matplotlib.axes.Axes` instance the artist
|
|
resides in, or *None*.
|
|
"""
|
|
return self._axes
|
|
|
|
@axes.setter
|
|
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 re-use 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
|
|
return new_axes
|
|
|
|
@property
|
|
def stale(self):
|
|
"""
|
|
If the artist is 'stale' and needs to be re-drawn for the output to
|
|
match the internal state of the artist.
|
|
"""
|
|
return self._stale
|
|
|
|
@stale.setter
|
|
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.get_animated():
|
|
return
|
|
|
|
if val and self.stale_callback is not None:
|
|
self.stale_callback(self, val)
|
|
|
|
def get_window_extent(self, renderer):
|
|
"""
|
|
Get the axes bounding box in display space.
|
|
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):
|
|
"""
|
|
Like `Artist.get_window_extent`, but includes any clipping.
|
|
|
|
Parameters
|
|
----------
|
|
renderer : `.RendererBase` instance
|
|
renderer that will be used to draw the figures (i.e.
|
|
``fig.canvas.get_renderer()``)
|
|
|
|
Returns
|
|
-------
|
|
bbox : `.BboxBase`
|
|
containing the bounding box (in figure pixel co-ordinates).
|
|
"""
|
|
|
|
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):
|
|
"""
|
|
Adds a callback function that will be called whenever one of
|
|
the :class:`Artist`'s properties changes.
|
|
|
|
Returns an *id* that is useful for removing the callback with
|
|
:meth:`remove_callback` later.
|
|
"""
|
|
oid = self._oid
|
|
self._propobservers[oid] = func
|
|
self._oid += 1
|
|
return oid
|
|
|
|
def remove_callback(self, oid):
|
|
"""
|
|
Remove a callback based on its *id*.
|
|
|
|
.. seealso::
|
|
|
|
:meth:`add_callback`
|
|
For adding callbacks
|
|
|
|
"""
|
|
try:
|
|
del self._propobservers[oid]
|
|
except KeyError:
|
|
pass
|
|
|
|
def pchanged(self):
|
|
"""
|
|
Fire an event when property changed, calling all of the
|
|
registered callbacks.
|
|
"""
|
|
for oid, func in self._propobservers.items():
|
|
func(self)
|
|
|
|
def is_transform_set(self):
|
|
"""
|
|
Returns *True* if :class:`Artist` has a transform explicitly
|
|
set.
|
|
"""
|
|
return self._transformSet
|
|
|
|
def set_transform(self, t):
|
|
"""
|
|
Set the artist transform.
|
|
|
|
Parameters
|
|
----------
|
|
t : `.Transform`
|
|
"""
|
|
self._transform = t
|
|
self._transformSet = True
|
|
self.pchanged()
|
|
self.stale = True
|
|
|
|
def get_transform(self):
|
|
"""
|
|
Return the :class:`~matplotlib.transforms.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
|
|
|
|
@cbook.deprecated("2.2")
|
|
def hitlist(self, event):
|
|
"""
|
|
List the children of the artist which contain the mouse event *event*.
|
|
"""
|
|
L = []
|
|
try:
|
|
hascursor, info = self.contains(event)
|
|
if hascursor:
|
|
L.append(self)
|
|
except:
|
|
import traceback
|
|
traceback.print_exc()
|
|
print("while checking", self.__class__)
|
|
|
|
for a in self.get_children():
|
|
L.extend(a.hitlist(event))
|
|
return L
|
|
|
|
def get_children(self):
|
|
"""
|
|
Return a list of the child :class:`Artist`s this
|
|
:class:`Artist` contains.
|
|
"""
|
|
return []
|
|
|
|
def contains(self, mouseevent):
|
|
"""Test whether the artist contains the mouse event.
|
|
|
|
Returns the truth value and a dictionary of artist specific details of
|
|
selection, such as which points are contained in the pick radius. See
|
|
individual artists for details.
|
|
"""
|
|
if callable(self._contains):
|
|
return self._contains(self, mouseevent)
|
|
warnings.warn("'%s' needs 'contains' method" % self.__class__.__name__)
|
|
return False, {}
|
|
|
|
def set_contains(self, picker):
|
|
"""
|
|
Replace the contains test used by this artist. The new picker
|
|
should be a callable function which determines whether the
|
|
artist is hit by the mouse event::
|
|
|
|
hit, props = picker(artist, mouseevent)
|
|
|
|
If the mouse event is over the artist, return *hit* = *True*
|
|
and *props* is a dictionary of properties you want returned
|
|
with the contains test.
|
|
|
|
Parameters
|
|
----------
|
|
picker : callable
|
|
"""
|
|
self._contains = picker
|
|
|
|
def get_contains(self):
|
|
"""
|
|
Return the _contains test used by the artist, or *None* for default.
|
|
"""
|
|
return self._contains
|
|
|
|
def pickable(self):
|
|
'Return *True* if :class:`Artist` is pickable.'
|
|
return (self.figure is not None and
|
|
self.figure.canvas is not None and
|
|
self._picker is not None)
|
|
|
|
def pick(self, mouseevent):
|
|
"""
|
|
Process pick event
|
|
|
|
each child artist will fire a pick event if *mouseevent* is over
|
|
the artist and the artist has picker set
|
|
"""
|
|
# 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:
|
|
self.figure.canvas.pick_event(mouseevent, self, **prop)
|
|
|
|
# Pick children
|
|
for a in self.get_children():
|
|
# make sure the event happened in the same axes
|
|
ax = getattr(a, 'axes', None)
|
|
if (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 no have an axes property but children might
|
|
a.pick(mouseevent)
|
|
|
|
def set_picker(self, picker):
|
|
"""
|
|
Set the epsilon for picking used by this artist
|
|
|
|
*picker* 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 it's 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.
|
|
|
|
Parameters
|
|
----------
|
|
picker : None or bool or float or callable
|
|
"""
|
|
self._picker = picker
|
|
|
|
def get_picker(self):
|
|
"""Return the picker object used by this artist."""
|
|
return self._picker
|
|
|
|
@cbook.deprecated("2.2", "artist.figure is not None")
|
|
def is_figure_set(self):
|
|
"""Returns whether the artist is assigned to a `.Figure`."""
|
|
return self.figure is not None
|
|
|
|
def get_url(self):
|
|
"""Returns the url."""
|
|
return self._url
|
|
|
|
def set_url(self, url):
|
|
"""
|
|
Sets the url for the artist.
|
|
|
|
Parameters
|
|
----------
|
|
url : str
|
|
"""
|
|
self._url = url
|
|
|
|
def get_gid(self):
|
|
"""Returns the group id."""
|
|
return self._gid
|
|
|
|
def set_gid(self, gid):
|
|
"""
|
|
Sets the (group) id for the artist.
|
|
|
|
Parameters
|
|
----------
|
|
gid : str
|
|
"""
|
|
self._gid = gid
|
|
|
|
def get_snap(self):
|
|
"""
|
|
Returns the snap setting which may be:
|
|
|
|
* True: snap vertices to the nearest pixel center
|
|
|
|
* False: leave vertices as-is
|
|
|
|
* None: (auto) If the path contains only rectilinear line
|
|
segments, round to the nearest pixel center
|
|
|
|
Only supported by the Agg and MacOSX backends.
|
|
"""
|
|
if rcParams['path.snap']:
|
|
return self._snap
|
|
else:
|
|
return False
|
|
|
|
def set_snap(self, snap):
|
|
"""
|
|
Sets the snap setting which may be:
|
|
|
|
* True: snap vertices to the nearest pixel center
|
|
|
|
* False: leave vertices as-is
|
|
|
|
* None: (auto) If the path contains only rectilinear line
|
|
segments, round to the nearest pixel center
|
|
|
|
Only supported by the Agg and MacOSX backends.
|
|
|
|
Parameters
|
|
----------
|
|
snap : bool or None
|
|
"""
|
|
self._snap = snap
|
|
self.stale = True
|
|
|
|
def get_sketch_params(self):
|
|
"""
|
|
Returns the sketch parameters for the artist.
|
|
|
|
Returns
|
|
-------
|
|
sketch_params : 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.
|
|
|
|
May return `None` if no sketch parameters were set.
|
|
"""
|
|
return self._sketch
|
|
|
|
def set_sketch_params(self, scale=None, length=None, randomness=None):
|
|
"""
|
|
Sets 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)
|
|
|
|
.. 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 : `.AbstractPathEffect`
|
|
"""
|
|
self._path_effects = path_effects
|
|
self.stale = True
|
|
|
|
def get_path_effects(self):
|
|
return self._path_effects
|
|
|
|
def get_figure(self):
|
|
"""Return the `.Figure` instance the artist belongs to."""
|
|
return self.figure
|
|
|
|
def set_figure(self, fig):
|
|
"""
|
|
Set the `.Figure` instance the artist belongs to.
|
|
|
|
Parameters
|
|
----------
|
|
fig : `.Figure`
|
|
"""
|
|
# if this is a no-op just return
|
|
if self.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.figure is not None:
|
|
raise RuntimeError("Can not put single artist in "
|
|
"more than one figure")
|
|
self.figure = fig
|
|
if self.figure and self.figure is not self:
|
|
self.pchanged()
|
|
self.stale = True
|
|
|
|
def set_clip_box(self, clipbox):
|
|
"""
|
|
Set the artist's clip `.Bbox`.
|
|
|
|
Parameters
|
|
----------
|
|
clipbox : `.Bbox`
|
|
"""
|
|
self.clipbox = clipbox
|
|
self.pchanged()
|
|
self.stale = True
|
|
|
|
def set_clip_path(self, path, transform=None):
|
|
"""
|
|
Set the artist's clip path, which may be:
|
|
|
|
- a :class:`~matplotlib.patches.Patch` (or subclass) instance; or
|
|
- a :class:`~matplotlib.path.Path` instance, in which case a
|
|
:class:`~matplotlib.transforms.Transform` instance, which will be
|
|
applied to the path before using it for clipping, must be provided;
|
|
or
|
|
- ``None``, to remove a previously set clipping path.
|
|
|
|
For efficiency, if the path happens to be an axis-aligned rectangle,
|
|
this method will set the clipping box to the corresponding rectangle
|
|
and set the clipping path to ``None``.
|
|
|
|
ACCEPTS: [(`~matplotlib.path.Path`, `.Transform`) | `.Patch` | 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 {} and {}"
|
|
.format(type(path).__name__, 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 artist's visiblity"
|
|
return self._visible
|
|
|
|
def get_animated(self):
|
|
"Return the artist's animated state"
|
|
return self._animated
|
|
|
|
def get_in_layout(self):
|
|
"""
|
|
Return boolean flag, ``True`` if artist is included in layout
|
|
calculations.
|
|
|
|
E.g. :doc:`/tutorials/intermediate/constrainedlayout_guide`,
|
|
`.Figure.tight_layout()`, and
|
|
``fig.savefig(fname, bbox_inches='tight')``.
|
|
"""
|
|
return self._in_layout
|
|
|
|
def get_clip_on(self):
|
|
'Return whether artist uses clipping'
|
|
return self._clipon
|
|
|
|
def get_clip_box(self):
|
|
'Return artist clipbox'
|
|
return self.clipbox
|
|
|
|
def get_clip_path(self):
|
|
'Return artist 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 artist uses clipping.
|
|
|
|
When False artists will be visible out side of 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 in vector backend output.
|
|
|
|
Defaults to None, which implies the backend's default behavior.
|
|
|
|
Parameters
|
|
----------
|
|
rasterized : bool or None
|
|
"""
|
|
if rasterized and not hasattr(self.draw, "_supports_rasterization"):
|
|
warnings.warn("Rasterization of '%s' will be ignored" % self)
|
|
|
|
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, 3) float array and a dpi
|
|
value, and returns a (m, n, 3) array.
|
|
|
|
.. ACCEPTS: a filter function, which takes a (m, n, 3) float array
|
|
and a dpi value, and returns a (m, n, 3) array
|
|
"""
|
|
self._agg_filter = filter_func
|
|
self.stale = True
|
|
|
|
def draw(self, renderer, *args, **kwargs):
|
|
'Derived classes drawing method'
|
|
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
|
|
"""
|
|
self._alpha = alpha
|
|
self.pchanged()
|
|
self.stale = True
|
|
|
|
def set_visible(self, b):
|
|
"""
|
|
Set the artist's visibility.
|
|
|
|
Parameters
|
|
----------
|
|
b : bool
|
|
"""
|
|
self._visible = b
|
|
self.pchanged()
|
|
self.stale = True
|
|
|
|
def set_animated(self, b):
|
|
"""
|
|
Set the artist's animation state.
|
|
|
|
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. :doc:`/tutorials/intermediate/constrainedlayout_guide`,
|
|
`.Figure.tight_layout()`, and
|
|
``fig.savefig(fname, bbox_inches='tight')``.
|
|
|
|
Parameters
|
|
----------
|
|
in_layout : bool
|
|
"""
|
|
self._in_layout = in_layout
|
|
|
|
def update(self, props):
|
|
"""
|
|
Update this artist's properties from the dictionary *prop*.
|
|
"""
|
|
def _update_property(self, k, v):
|
|
"""Sorting out how to update property (setter or setattr).
|
|
|
|
Parameters
|
|
----------
|
|
k : str
|
|
The name of property to update
|
|
v : obj
|
|
The value to assign to the property
|
|
|
|
Returns
|
|
-------
|
|
ret : obj or None
|
|
If using a `set_*` method return it's return, else None.
|
|
"""
|
|
k = k.lower()
|
|
# white list attributes we want to be able to update through
|
|
# art.update, art.set, setp
|
|
if k in {'axes'}:
|
|
return setattr(self, k, v)
|
|
else:
|
|
func = getattr(self, 'set_' + k, None)
|
|
if not callable(func):
|
|
raise AttributeError('Unknown property %s' % k)
|
|
return func(v)
|
|
|
|
with cbook._setattr_cm(self, eventson=False):
|
|
ret = [_update_property(self, k, v) for k, v in props.items()]
|
|
|
|
if len(ret):
|
|
self.pchanged()
|
|
self.stale = True
|
|
return ret
|
|
|
|
def get_label(self):
|
|
"""Get the label used for this artist in the legend."""
|
|
return self._label
|
|
|
|
def set_label(self, s):
|
|
"""
|
|
Set the label to *s* for auto legend.
|
|
|
|
Parameters
|
|
----------
|
|
s : object
|
|
*s* will be converted to a string by calling `str`.
|
|
"""
|
|
if s is not None:
|
|
self._label = str(s)
|
|
else:
|
|
self._label = None
|
|
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
|
|
self.zorder = level
|
|
self.pchanged()
|
|
self.stale = True
|
|
|
|
@property
|
|
def sticky_edges(self):
|
|
"""
|
|
`x` and `y` sticky edge lists.
|
|
|
|
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 usecase is histograms,
|
|
where one usually expects no margin on the bottom edge (0) of the
|
|
histogram.
|
|
|
|
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[:]
|
|
self.sticky_edges.y[:] = other.sticky_edges.y[:]
|
|
self.pchanged()
|
|
self.stale = True
|
|
|
|
def properties(self):
|
|
"""
|
|
return a dictionary mapping property name -> value for all Artist props
|
|
"""
|
|
return ArtistInspector(self).properties()
|
|
|
|
def set(self, **kwargs):
|
|
"""A property batch setter. Pass *kwargs* to set properties.
|
|
"""
|
|
props = OrderedDict(
|
|
sorted(kwargs.items(), reverse=True,
|
|
key=lambda x: (self._prop_order.get(x[0], 0), x[0])))
|
|
|
|
return self.update(props)
|
|
|
|
def findobj(self, match=None, include_self=True):
|
|
"""
|
|
Find artist objects.
|
|
|
|
Recursively find all :class:`~matplotlib.artist.Artist` instances
|
|
contained in self.
|
|
|
|
*match* can be
|
|
|
|
- None: return all objects contained in artist.
|
|
|
|
- function with signature ``boolean = match(artist)``
|
|
used to filter matches
|
|
|
|
- class instance: e.g., Line2D. Only return artists of class type.
|
|
|
|
If *include_self* is True (default), include self in the list to be
|
|
checked for a match.
|
|
|
|
"""
|
|
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 = sum([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):
|
|
"""
|
|
Get the cursor data for a given event.
|
|
"""
|
|
return None
|
|
|
|
def format_cursor_data(self, data):
|
|
"""
|
|
Return *cursor data* string formatted.
|
|
"""
|
|
try:
|
|
data[0]
|
|
except (TypeError, IndexError):
|
|
data = [data]
|
|
data_str = ', '.join('{:0.3g}'.format(item) for item in data if
|
|
isinstance(item, (np.floating, np.integer, int, float)))
|
|
return "[" + data_str + "]"
|
|
|
|
@property
|
|
def mouseover(self):
|
|
return self._mouseover
|
|
|
|
@mouseover.setter
|
|
def mouseover(self, val):
|
|
val = bool(val)
|
|
self._mouseover = val
|
|
ax = self.axes
|
|
if ax:
|
|
if val:
|
|
ax._mouseover_set.add(self)
|
|
else:
|
|
ax._mouseover_set.discard(self)
|
|
|
|
|
|
class ArtistInspector(object):
|
|
"""
|
|
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 `Artists` are of the same type) and it is your
|
|
responsibility to make sure this is so.
|
|
"""
|
|
if not isinstance(o, Artist):
|
|
if cbook.iterable(o):
|
|
o = list(o)
|
|
if len(o):
|
|
o = o[0]
|
|
|
|
self.oorig = o
|
|
if not inspect.isclass(o):
|
|
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("`({}.*)`".format(name[:4]), # get_.*/set_.*
|
|
func.__doc__).group(1)
|
|
aliases.setdefault(propname, 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 function *set_attr*
|
|
for a line that begins with "ACCEPTS" or ".. ACCEPTS":
|
|
|
|
e.g., for a line linestyle, return
|
|
"[ ``'-'`` | ``'--'`` | ``'-.'`` | ``':'`` | ``'steps'`` | ``'None'``
|
|
]"
|
|
"""
|
|
|
|
name = 'set_%s' % attr
|
|
if not hasattr(self.o, name):
|
|
raise AttributeError('%s has no function %s' % (self.o, name))
|
|
func = getattr(self.o, name)
|
|
|
|
docstring = func.__doc__
|
|
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]
|
|
match = re.search("(?m)^ *{} : (.+)".format(param_name), docstring)
|
|
if match:
|
|
return match.group(1)
|
|
|
|
return 'unknown'
|
|
|
|
def _get_setters_and_targets(self):
|
|
"""
|
|
Get the attribute strings and a full path to where the setter
|
|
is defined for all setters in an object.
|
|
"""
|
|
|
|
setters = []
|
|
for name in dir(self.o):
|
|
if not name.startswith('set_'):
|
|
continue
|
|
func = getattr(self.o, name)
|
|
if not callable(func):
|
|
continue
|
|
nargs = len(inspect.getfullargspec(func).args)
|
|
if nargs < 2 or self.is_alias(func):
|
|
continue
|
|
source_class = self.o.__module__ + "." + self.o.__name__
|
|
for cls in self.o.mro():
|
|
if name in cls.__dict__:
|
|
source_class = cls.__module__ + "." + cls.__name__
|
|
break
|
|
source_class = self._replace_path(source_class)
|
|
setters.append((name[4:], source_class + "." + name))
|
|
return setters
|
|
|
|
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. e.g., for a line,
|
|
return ``['markerfacecolor', 'linewidth', ....]``.
|
|
"""
|
|
|
|
return [prop for prop, target in self._get_setters_and_targets()]
|
|
|
|
def is_alias(self, o):
|
|
"""
|
|
Return *True* if method object *o* is an alias for another
|
|
function.
|
|
"""
|
|
ds = o.__doc__
|
|
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.
|
|
|
|
e.g., for the line markerfacecolor property, which has an
|
|
alias, return 'markerfacecolor or mfc' and for the transform
|
|
property, which does not, return 'transform'
|
|
"""
|
|
|
|
if s in self.aliasd:
|
|
return s + ''.join([' or %s' % x
|
|
for x in sorted(self.aliasd[s])])
|
|
else:
|
|
return s
|
|
|
|
def aliased_name_rest(self, s, target):
|
|
"""
|
|
return 'PROPNAME or alias' if *s* has an alias, else return
|
|
PROPNAME formatted for ReST
|
|
|
|
e.g., for the line markerfacecolor property, which has an
|
|
alias, return 'markerfacecolor or mfc' and for the transform
|
|
property, which does not, return 'transform'
|
|
"""
|
|
|
|
if s in self.aliasd:
|
|
aliases = ''.join([' or %s' % x
|
|
for x in sorted(self.aliasd[s])])
|
|
else:
|
|
aliases = ''
|
|
return ':meth:`%s <%s>`%s' % (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 '%s%s: %s' % (pad, prop, accepts)
|
|
|
|
attrs = self._get_setters_and_targets()
|
|
attrs.sort()
|
|
lines = []
|
|
|
|
for prop, path in attrs:
|
|
accepts = self.get_valid_values(prop)
|
|
name = self.aliased_name(prop)
|
|
|
|
lines.append('%s%s: %s' % (pad, name, accepts))
|
|
return lines
|
|
|
|
def pprint_setters_rest(self, prop=None, leadingspace=4):
|
|
"""
|
|
If *prop* is *None*, return a list of strings of all settable
|
|
properties and their valid values. Format the output for ReST
|
|
|
|
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 '%s%s: %s' % (pad, prop, accepts)
|
|
|
|
attrs = self._get_setters_and_targets()
|
|
attrs.sort()
|
|
lines = []
|
|
|
|
########
|
|
names = [self.aliased_name_rest(prop, target)
|
|
for prop, target in attrs]
|
|
accepts = [self.get_valid_values(prop) for prop, target in attrs]
|
|
|
|
col0_len = max(len(n) for n in names)
|
|
col1_len = max(len(a) for a in accepts)
|
|
|
|
lines.append('')
|
|
lines.append(pad + '.. table::')
|
|
lines.append(pad + ' :class: property-table')
|
|
pad += ' '
|
|
|
|
table_formatstr = pad + '=' * col0_len + ' ' + '=' * col1_len
|
|
|
|
lines.append('')
|
|
lines.append(table_formatstr)
|
|
lines.append(pad + 'Property'.ljust(col0_len + 3) +
|
|
'Description'.ljust(col1_len))
|
|
lines.append(table_formatstr)
|
|
|
|
lines.extend([pad + n.ljust(col0_len + 3) + a.ljust(col1_len)
|
|
for n, a in zip(names, accepts)])
|
|
|
|
lines.append(table_formatstr)
|
|
lines.append('')
|
|
return lines
|
|
|
|
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 = dict()
|
|
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:
|
|
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(' %s = %s' % (name, s))
|
|
return lines
|
|
|
|
|
|
def getp(obj, property=None):
|
|
"""
|
|
Return the value of object's property. *property* is an optional string
|
|
for the property you want to return
|
|
|
|
Example usage::
|
|
|
|
getp(obj) # get all the object properties
|
|
getp(obj, 'linestyle') # get the linestyle property
|
|
|
|
*obj* is a :class:`Artist` instance, e.g.,
|
|
:class:`~matplotllib.lines.Line2D` or an instance of a
|
|
:class:`~matplotlib.axes.Axes` or :class:`matplotlib.text.Text`.
|
|
If the *property* is 'somename', this function returns
|
|
|
|
obj.get_somename()
|
|
|
|
:func:`getp` can be used to query all the gettable properties with
|
|
``getp(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
|
|
"""
|
|
if property is None:
|
|
insp = ArtistInspector(obj)
|
|
ret = insp.pprint_getters()
|
|
print('\n'.join(ret))
|
|
return
|
|
|
|
func = getattr(obj, 'get_' + property)
|
|
return func()
|
|
|
|
# alias
|
|
get = getp
|
|
|
|
|
|
def setp(obj, *args, **kwargs):
|
|
"""
|
|
Set a property on an artist object.
|
|
|
|
matplotlib supports the use of :func:`setp` ("set property") and
|
|
:func:`getp` to set and get object properties, as well as to do
|
|
introspection on the object. For example, to set the linestyle of a
|
|
line to be dashed, you can do::
|
|
|
|
>>> line, = plot([1,2,3])
|
|
>>> setp(line, linestyle='--')
|
|
|
|
If you want to know the valid types of arguments, you can provide
|
|
the name of the property you want to set without a value::
|
|
|
|
>>> setp(line, 'linestyle')
|
|
linestyle: [ '-' | '--' | '-.' | ':' | 'steps' | 'None' ]
|
|
|
|
If you want to see all the properties that can be set, and their
|
|
possible values, you can do::
|
|
|
|
>>> setp(line)
|
|
... long output listing omitted
|
|
|
|
You may specify another output file to `setp` if `sys.stdout` is not
|
|
acceptable for some reason using the `file` keyword-only argument::
|
|
|
|
>>> with fopen('output.log') as f:
|
|
>>> setp(line, file=f)
|
|
|
|
:func:`setp` operates on a single instance or a iterable of
|
|
instances. If you are in query mode introspecting the possible
|
|
values, only the first instance in the sequence is used. When
|
|
actually setting values, all the instances will be set. e.g.,
|
|
suppose you have a list of two lines, the following will make both
|
|
lines thicker and red::
|
|
|
|
>>> x = arange(0,1.0,0.01)
|
|
>>> y1 = sin(2*pi*x)
|
|
>>> y2 = sin(4*pi*x)
|
|
>>> lines = plot(x, y1, x, y2)
|
|
>>> setp(lines, linewidth=2, color='r')
|
|
|
|
:func:`setp` works with the MATLAB style string/value pairs or
|
|
with python kwargs. For example, the following are equivalent::
|
|
|
|
>>> setp(lines, 'linewidth', 2, 'color', 'r') # MATLAB style
|
|
>>> setp(lines, linewidth=2, color='r') # python style
|
|
"""
|
|
|
|
if isinstance(obj, Artist):
|
|
objs = [obj]
|
|
else:
|
|
objs = list(cbook.flatten(obj))
|
|
|
|
if not objs:
|
|
return
|
|
|
|
insp = ArtistInspector(objs[0])
|
|
|
|
# file has to be popped before checking if kwargs is empty
|
|
printArgs = {}
|
|
if 'file' in kwargs:
|
|
printArgs['file'] = kwargs.pop('file')
|
|
|
|
if not kwargs and len(args) < 2:
|
|
if args:
|
|
print(insp.pprint_setters(prop=args[0]), **printArgs)
|
|
else:
|
|
print('\n'.join(insp.pprint_setters()), **printArgs)
|
|
return
|
|
|
|
if len(args) % 2:
|
|
raise ValueError('The set args must be string, value pairs')
|
|
|
|
# put args into ordereddict to maintain order
|
|
funcvals = OrderedDict((k, v) for k, v in 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 and return
|
|
information about its settable properties and their current values.
|
|
|
|
It use the class `.ArtistInspector`.
|
|
|
|
Parameters
|
|
----------
|
|
artist : `~matplotlib.artist.Artist` or an iterable of `Artist`\s
|
|
|
|
Returns
|
|
-------
|
|
string
|
|
Returns a string with a list or rst table with the settable properties
|
|
of the *artist*. The formating depends on the value of
|
|
:rc:`docstring.hardcopy`. False result in a list that is intended for
|
|
easy reading as a docstring and True result in a rst table intended
|
|
for rendering the documentation with sphinx.
|
|
"""
|
|
hardcopy = matplotlib.rcParams['docstring.hardcopy']
|
|
if hardcopy:
|
|
return '\n'.join(ArtistInspector(artist).pprint_setters_rest(
|
|
leadingspace=4))
|
|
else:
|
|
return '\n'.join(ArtistInspector(artist).pprint_setters(
|
|
leadingspace=2))
|
|
|
|
docstring.interpd.update(Artist=kwdoc(Artist))
|