Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Axesbase #1032

Closed
wants to merge 2 commits into from
+1,202 −1,042

4 participants

Benjamin Root Phil Elson Eric Firing Michael Droettboom
Benjamin Root
Collaborator

This is a rather involved and invasive PR, and isn't ready for merging. But I wanted to give others a chance to try it out and comment on what I have done.

Essentially, I want to segregate the plotting functions from the maintenance methods in the Axes class. There are several reasons for this, some of which is too involved to go into right now. A practical upshot of this is that the API page for axes.py is much smaller! Further, as I move methods over to AxesBase, I have been making them axis-agnostic. I want to eventually eliminate ax.xaxis and ax.yaxis in favor of ax.axis['x'] dictionaries. The idea is that most methods actually do not care about which axis objects to act on be made into a method that operates over an ordered dictionary of axis objects. This will allow for arbitrary names of axis and an arbitrary number of axis objects.

I have put in backwards-compatibility shims where needed. It is still intended that everyone should subclass the Axes class for their uses until this refactor is completed. And even after the refactor, everyone's existing code should still work if they are subclassing Axes instead of AxesBase.

There is only one caveat I could find. The compatibility shim I used for ax.xaxis and ax.yaxis is to define some class properties to intercept the gets and sets to the internal dictionary. This means that xaxis and yaxis exist from the onset of the Axes construction, whereas before, the xaxis and yaxis didn't exist until sometime during the Axes constructor. So, any code that checked for the existance of xaxis and yaxis before acting on them should be changed to check for None.

So,
hasattr(ax, 'xaxis')
should be changed to
ax.xaxis is not None

WeatherGod added some commits
Benjamin Root WeatherGod Initial effort to split out from the Axes class an AxesBase class 9a1e938
Benjamin Root WeatherGod More Axes-to-AxesBase refactor
 * Moved nearly all axis-agnostic methods to AxesBase
 * Added compatibility shims for self.xaxis and self.yaxis
 * Fixed PEP8 issues
 * Fixed outdated python-isms such as '<>'.
 * All hasattr(ax, 'xaxis') calls must now be replaced with
   ax.xaxis is not None and the same for yaxis.
cc91426
Phil Elson pelson commented on the diff
lib/matplotlib/axesbase.py
@@ -0,0 +1,1039 @@
+
+# We need this future import because we unfortunately have
+# a module name collision with the standard module called "collections".
+from __future__ import absolute_import
+from collections import defaultdict, OrderedDict
Phil Elson Collaborator
pelson added a note

can you not just do import matplotlib.collections?

Benjamin Root Collaborator

You are getting mixed up. I want defaultdict and OrderedDict from the standard library's "collections" module, not matplotlib's. But because axesbase.py is in the same directory as matplotlib's collections.py, pre-py3k import rules will accidentially grab matplotlib's collections module. It is for this exact reason that implicit relative imports is being deprecated.

Phil Elson Collaborator
pelson added a note

Ah yes. Ignore me, I misread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Phil Elson pelson commented on the diff
lib/matplotlib/axesbase.py
((35 lines not shown))
+ :class:`~matplotlib.axes.Axes` class into their own base class.
+ These methods are largely generalized maintenance operations and
+ other book-keeping actions. Ultimately, the objective is for the
+ Axes class will contain only pure plotting methods.
+
+ It is still intended for developers and extension writers to continue
+ subclassing the Axes class, and no existing code will be broken by
+ this migration effort.
+
+ """
+
+ _shared_axes = defaultdict(cbook.Grouper)
+ _axis_classes = {}
+ _axis_locs = {}
+
+ def __init__(self, fig, rect, axis_names,
Phil Elson Collaborator
pelson added a note

Would need a docstring.

Benjamin Root Collaborator

Yes, I plan on moving over a significant portion of Axes's docstring over to here, and have Axes's docstring be built partly from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Phil Elson
Collaborator

Do the tests still pass?

This is a rather involved and invasive PR

Yes, do you have you sights set on subclassing AxesBase for mplot3d? Otherwise, I cannot see that any 2d Axes subclass would want to subclass anything below Axes.

I want to eventually eliminate ax.xaxis and ax.yaxis in favor of ax.axis['x'] dictionaries

I think that is a worthwhile ambition, but I believe it could be done without such an invasive change (as could splitting the Axes class documentation).

Despite my comments, I actually don't have a problem with this change, although it would be helpful/interesting to know some of your other motivations.

Benjamin Root
Collaborator

All but one test pass, but that one seems to be a minor rendering error with stix fonts. My motivations with mplot3d is that I eventually want to be able to create 3d axes objects by taking any of the 2D axes, and sticking a 3rd dimension on them. Imagine being able to take a polar plot or one of the geo axes and have a z-axis added on to it. Such an axes would then implicitly inherit all of that axes's plotting functions. Furthermore, in mplot3d, I find myself having to copy-n-paste many axes methods just for the sake of the third axis, and then that code gets out of date or a transcription error happened, rendering it as broken code. Furthermore, there are several axes methods that performs an action for explicitly the x and y axis objects. mplot3d then has to over-ride that function, call it, and then add one more instance of that code just for the zaxis. The same issues apply here with transcription errors and out-of-date code. We should have to maintain this stuff in two places.

There is also discussion that the plotting functions will eventually be turned into something more like mathmatica's and chacao's plotting elements, so by working to separate out the plotting and maintenance portions of Axes would be a first step towards this goal. And yes, I do eventually intend for AxesBase to be directly subclassed by others eventually.

Eric Firing
Owner

I like the idea of separating the basic Axes functionality from all the plotting machinery, and would prefer to see more of the plotting machinery broken out into related chunks, as with quiver and contour, for example. So I think there is justification for what you are doing here even without mplot3d.

As for mplot3d, I worry about continuing to pour resources (your time, mostly) into an approach that is fundamentally a hack to simulate 3d, and that therefore has major inherent limitations. Granted, it has quite a few users, judging from the email traffic.

Benjamin Root
Collaborator

I am going to close this PR of mine, and turn it into a MEP instead. These concepts need to be fleshed out better in planning.

Benjamin Root WeatherGod closed this
Michael Droettboom
Owner

I think this a great idea for a MEP. What's here is already quite good.

I'm sorry I haven't commented on this until now -- I had meant to and it slipped by me. This is something I've thought should be done for a long time, so it's great to see the effort on it.

One thing to consider as you draft the MEP: I've long thought that it would be helpful to group the plotting methods into categories, at least as to where their implementations are, to prevent this sort of "enormous file with no logical ordering" property we have now. We already do this -- sort of -- with the contour and triangulation methods. I'd like to see more of that, for example a module for bar plots, one for pcolor/pcolormesh and friends, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 19, 2012
  1. Benjamin Root
Commits on Jul 20, 2012
  1. Benjamin Root

    More Axes-to-AxesBase refactor

    WeatherGod authored
     * Moved nearly all axis-agnostic methods to AxesBase
     * Added compatibility shims for self.xaxis and self.yaxis
     * Fixed PEP8 issues
     * Fixed outdated python-isms such as '<>'.
     * All hasattr(ax, 'xaxis') calls must now be replaced with
       ax.xaxis is not None and the same for yaxis.
This page is out of date. Refresh to see the latest.
Showing with 1,202 additions and 1,042 deletions.
  1. +159 −1,038 lib/matplotlib/axes.py
  2. +1,039 −0 lib/matplotlib/axesbase.py
  3. +4 −4 lib/matplotlib/axis.py
1,197 lib/matplotlib/axes.py
View
@@ -9,6 +9,7 @@
import matplotlib
rcParams = matplotlib.rcParams
+from matplotlib.axesbase import AxesBase
import matplotlib.artist as martist
from matplotlib.artist import allow_rasterization
import matplotlib.axis as maxis
@@ -42,14 +43,8 @@
is_string_like = cbook.is_string_like
is_sequence_of_strings = cbook.is_sequence_of_strings
-def _string_to_bool(s):
- if not is_string_like(s):
- return s
- if s == 'on':
- return True
- if s == 'off':
- return False
- raise ValueError("string argument must be either 'on' or 'off'")
+# for API Maintence
+from matplotlib.axesbase import _string_to_bool
def _process_plot_format(fmt):
"""
@@ -334,7 +329,7 @@ def _grab_next_args(self, *args, **kwargs):
remaining=remaining[isplit:]
-class Axes(martist.Artist):
+class Axes(AxesBase):
"""
The :class:`Axes` contains most of the figure elements:
:class:`~matplotlib.axis.Axis`, :class:`~matplotlib.axis.Tick`,
@@ -350,16 +345,24 @@ class Axes(martist.Artist):
"""
name = "rectilinear"
- _shared_x_axes = cbook.Grouper()
- _shared_y_axes = cbook.Grouper()
+ # TODO: For compatibility purposes
+ _shared_x_axes = AxesBase._shared_axes['x']
+ _shared_y_axes = AxesBase._shared_axes['y']
+ _axis_classes = dict(AxesBase._axis_classes)
+ _axis_classes['x'] = maxis.XAxis
+ _axis_classes['y'] = maxis.YAxis
+ _axis_locs = dict(AxesBase._axis_locs)
+ _axis_locs['x'] = ('bottom', 'top')
+ _axis_locs['y'] = ('left', 'right')
+
def __str__(self):
return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
def __init__(self, fig, rect,
- axisbg = None, # defaults to rc axes.facecolor
- frameon = True,
- sharex=None, # use Axes instance's xaxis info
- sharey=None, # use Axes instance's yaxis info
+ axisbg=None, # defaults to rc axes.facecolor
+ frameon=True,
+ sharex=None, # use axes instance's xaxis info
+ sharey=None, # use axes instance's yaxis info
label='',
xscale=None,
yscale=None,
@@ -416,38 +419,11 @@ def __init__(self, fig, rect,
*yticks* sequence of floats
================ =========================================
""" % {'scale': ' | '.join([repr(x) for x in mscale.get_scale_names()])}
- martist.Artist.__init__(self)
- if isinstance(rect, mtransforms.Bbox):
- self._position = rect
- else:
- self._position = mtransforms.Bbox.from_bounds(*rect)
- self._originalPosition = self._position.frozen()
- self.set_axes(self)
- self.set_aspect('auto')
- self._adjustable = 'box'
- self.set_anchor('C')
- self._sharex = sharex
- self._sharey = sharey
- if sharex is not None:
- self._shared_x_axes.join(self, sharex)
- if sharex._adjustable == 'box':
- sharex._adjustable = 'datalim'
- #warnings.warn(
- # 'shared axes: "adjustable" is being changed to "datalim"')
- self._adjustable = 'datalim'
- if sharey is not None:
- self._shared_y_axes.join(self, sharey)
- if sharey._adjustable == 'box':
- sharey._adjustable = 'datalim'
- #warnings.warn(
- # 'shared axes: "adjustable" is being changed to "datalim"')
- self._adjustable = 'datalim'
- self.set_label(label)
- self.set_figure(fig)
-
- self.set_axes_locator(kwargs.get("axes_locator", None))
-
- self.spines = self._gen_axes_spines()
+ AxesBase.__init__(self, fig, rect, ('x', 'y'), axisbg, frameon, label,
+ share={'x':sharex, 'y':sharey},
+ scale={'x':xscale, 'y':yscale},
+ axes_locator=kwargs.get('axes_locator', None),
+ )
# this call may differ for non-sep axes, eg polar
self._init_axis()
@@ -496,49 +472,60 @@ def get_window_extent(self, *args, **kwargs):
"""
return self.bbox
- def _init_axis(self):
- "move this out of __init__ because non-separable axes don't use it"
- self.xaxis = maxis.XAxis(self)
- self.spines['bottom'].register_axis(self.xaxis)
- self.spines['top'].register_axis(self.xaxis)
- self.yaxis = maxis.YAxis(self)
- self.spines['left'].register_axis(self.yaxis)
- self.spines['right'].register_axis(self.yaxis)
- self._update_transScale()
-
- def set_figure(self, fig):
+ # A compatibility shim. We need to intercept any gets and sets of
+ # xaxis and yaxis, which are the traditional names for the axis objects
+ # Note that this doesn't fully help if someone subclassed axes in such
+ # a way that a completely different name was used, but in such a case,
+ # most likely the user would have over-ridden most of everything else
+ # as well.
+ @property
+ def xaxis(self):
+ """
+ The x-axis object for this Axes.
"""
- Set the class:`~matplotlib.axes.Axes` figure
+ return self._axis_objs['x']
+
+ @xaxis.setter
+ def xaxis(self, val):
+ self._axis_objs['x'] = val
- accepts a class:`~matplotlib.figure.Figure` instance
+ @xaxis.deleter
+ def xaxis(self):
+ del self._axis_objs['x']
+
+ @property
+ def yaxis(self):
+ """
+ The y-axis object for this Axes.
"""
- martist.Artist.set_figure(self, fig)
+ return self._axis_objs['y']
- self.bbox = mtransforms.TransformedBbox(self._position, fig.transFigure)
- #these will be updated later as data is added
- self.dataLim = mtransforms.Bbox.unit()
- self.viewLim = mtransforms.Bbox.unit()
- self.transScale = mtransforms.TransformWrapper(
- mtransforms.IdentityTransform())
+ @yaxis.setter
+ def yaxis(self, val):
+ self._axis_objs['y'] = val
+
+ @yaxis.deleter
+ def yaxis(self):
+ del self._axis_objs['y']
- self._set_lim_and_transforms()
+
+ #def _init_axis(self):
+ # self.xaxis = self._axis_classes['x'](self)
+ # self.spines['bottom'].register_axis(self.xaxis)
+ # self.spines['top'].register_axis(self.xaxis)
+ # self.yaxis = self._axis_classes['y'](self)
+ # self.spines['left'].register_axis(self.yaxis)
+ # self.spines['right'].register_axis(self.yaxis)
+
+ # self._update_transScale()
def _set_lim_and_transforms(self):
"""
- set the *dataLim* and *viewLim*
+ Set the *dataLim* and *viewLim*
:class:`~matplotlib.transforms.Bbox` attributes and the
*transScale*, *transData*, *transLimits* and *transAxes*
transformations.
- .. note::
-
- This method is primarily used by rectilinear projections
- of the :class:`~matplotlib.axes.Axes` class, and is meant
- to be overridden by new kinds of projection axes that need
- different transformations and limits. (See
- :class:`~matplotlib.projections.polar.PolarAxes` for an
- example.
-
"""
self.transAxes = mtransforms.BboxTransformTo(self.bbox)
@@ -563,7 +550,12 @@ def _set_lim_and_transforms(self):
self._yaxis_transform = mtransforms.blended_transform_factory(
self.transAxes, self.transData)
- def get_xaxis_transform(self,which='grid'):
+ # TODO: Migration shim
+ self._axis_transforms['x'] = self._xaxis_transform
+ self._axis_transforms['y'] = self._yaxis_transform
+
+
+ def get_xaxis_transform(self, which='grid'):
"""
Get the transformation used for drawing x-axis labels, ticks
and gridlines. The x-direction is in data coordinates and the
@@ -577,16 +569,7 @@ def get_xaxis_transform(self,which='grid'):
place axis elements in different locations.
"""
- if which=='grid':
- return self._xaxis_transform
- elif which=='tick1':
- # for cartesian projection, this is bottom spine
- return self.spines['bottom'].get_spine_transform()
- elif which=='tick2':
- # for cartesian projection, this is top spine
- return self.spines['top'].get_spine_transform()
- else:
- raise ValueError('unknown value for which')
+ return self._get_axis_transform(which, 'x')
def get_xaxis_text1_transform(self, pad_points):
"""
@@ -640,7 +623,7 @@ def get_xaxis_text2_transform(self, pad_points):
self.figure.dpi_scale_trans),
"bottom", "center")
- def get_yaxis_transform(self,which='grid'):
+ def get_yaxis_transform(self, which='grid'):
"""
Get the transformation used for drawing y-axis labels, ticks
and gridlines. The x-direction is in axis coordinates and the
@@ -654,16 +637,7 @@ def get_yaxis_transform(self,which='grid'):
place axis elements in different locations.
"""
- if which=='grid':
- return self._yaxis_transform
- elif which=='tick1':
- # for cartesian projection, this is bottom spine
- return self.spines['left'].get_spine_transform()
- elif which=='tick2':
- # for cartesian projection, this is top spine
- return self.spines['right'].get_spine_transform()
- else:
- raise ValueError('unknown value for which')
+ return self._get_axis_transform(which, 'y')
def get_yaxis_text1_transform(self, pad_points):
"""
@@ -717,86 +691,6 @@ def get_yaxis_text2_transform(self, pad_points):
self.figure.dpi_scale_trans),
"center", "left")
- def _update_transScale(self):
- self.transScale.set(
- mtransforms.blended_transform_factory(
- self.xaxis.get_transform(), self.yaxis.get_transform()))
- if hasattr(self, "lines"):
- for line in self.lines:
- try:
- line._transformed_path.invalidate()
- except AttributeError:
- pass
-
- def get_position(self, original=False):
- 'Return the a copy of the axes rectangle as a Bbox'
- if original:
- return self._originalPosition.frozen()
- else:
- return self._position.frozen()
-
-
- def set_position(self, pos, which='both'):
- """
- Set the axes position with::
-
- pos = [left, bottom, width, height]
-
- in relative 0,1 coords, or *pos* can be a
- :class:`~matplotlib.transforms.Bbox`
-
- There are two position variables: one which is ultimately
- used, but which may be modified by :meth:`apply_aspect`, and a
- second which is the starting point for :meth:`apply_aspect`.
-
-
- Optional keyword arguments:
- *which*
-
- ========== ====================
- value description
- ========== ====================
- 'active' to change the first
- 'original' to change the second
- 'both' to change both
- ========== ====================
-
- """
- if not isinstance(pos, mtransforms.BboxBase):
- pos = mtransforms.Bbox.from_bounds(*pos)
- if which in ('both', 'active'):
- self._position.set(pos)
- if which in ('both', 'original'):
- self._originalPosition.set(pos)
-
- def reset_position(self):
- """Make the original position the active position"""
- pos = self.get_position(original=True)
- self.set_position(pos, which='active')
-
- def set_axes_locator(self, locator):
- """
- set axes_locator
-
- ACCEPT : a callable object which takes an axes instance and renderer and
- returns a bbox.
- """
- self._axes_locator = locator
-
- def get_axes_locator(self):
- """
- return axes_locator
- """
- return self._axes_locator
-
- def _set_artist_props(self, a):
- """set the boilerplate props for artists added to axes"""
- a.set_figure(self.figure)
- if not a.is_transform_set():
- a.set_transform(self.transData)
-
- a.set_axes(self)
-
def _gen_axes_patch(self):
"""
Returns the patch used to draw the background of the axes. It
@@ -814,25 +708,13 @@ def _gen_axes_patch(self):
return mpatches.Rectangle((0.0, 0.0), 1.0, 1.0)
def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'):
- """
- Returns a dict whose keys are spine names and values are
- Line2D or Patch instances. Each element is used to draw a
- spine of the axes.
-
- In the standard axes, this is a single line segment, but in
- other projections it may not be.
-
- .. note::
-
- Intended to be overridden by new projection types.
-
- """
return {
'left':mspines.Spine.linear_spine(self,'left'),
'right':mspines.Spine.linear_spine(self,'right'),
'bottom':mspines.Spine.linear_spine(self,'bottom'),
'top':mspines.Spine.linear_spine(self,'top'),
}
+ _gen_axes_spines.__doc__ = AxesBase._gen_axes_spines.__doc__
def cla(self):
"""Clear the current axes"""
@@ -941,141 +823,7 @@ def set_color_cycle(self, clist):
self._get_patches_for_fill.set_color_cycle(clist)
- def ishold(self):
- """return the HOLD status of the axes"""
- return self._hold
-
- def hold(self, b=None):
- """
- Call signature::
-
- hold(b=None)
-
- Set the hold state. If *hold* is *None* (default), toggle the
- *hold* state. Else set the *hold* state to boolean value *b*.
-
- Examples::
-
- # toggle hold
- hold()
-
- # turn hold on
- hold(True)
-
- # turn hold off
- hold(False)
-
-
- When hold is *True*, subsequent plot commands will be added to
- the current axes. When hold is *False*, the current axes and
- figure will be cleared on the next plot command
-
- """
- if b is None:
- self._hold = not self._hold
- else:
- self._hold = b
-
- def get_aspect(self):
- return self._aspect
-
- def set_aspect(self, aspect, adjustable=None, anchor=None):
- """
- *aspect*
-
- ======== ================================================
- value description
- ======== ================================================
- 'auto' automatic; fill position rectangle with data
- 'normal' same as 'auto'; deprecated
- 'equal' same scaling from data to plot units for x and y
- num a circle will be stretched such that the height
- is num times the width. aspect=1 is the same as
- aspect='equal'.
- ======== ================================================
-
- *adjustable*
-
- ============ =====================================
- value description
- ============ =====================================
- 'box' change physical size of axes
- 'datalim' change xlim or ylim
- 'box-forced' same as 'box', but axes can be shared
- ============ =====================================
-
- 'box' does not allow axes sharing, as this can cause
- unintended side effect. For cases when sharing axes is
- fine, use 'box-forced'.
-
- *anchor*
-
- ===== =====================
- value description
- ===== =====================
- 'C' centered
- 'SW' lower left corner
- 'S' middle of bottom edge
- 'SE' lower right corner
- etc.
- ===== =====================
-
- """
- if aspect in ('normal', 'auto'):
- self._aspect = 'auto'
- elif aspect == 'equal':
- self._aspect = 'equal'
- else:
- self._aspect = float(aspect) # raise ValueError if necessary
-
- if adjustable is not None:
- self.set_adjustable(adjustable)
- if anchor is not None:
- self.set_anchor(anchor)
-
- def get_adjustable(self):
- return self._adjustable
-
- def set_adjustable(self, adjustable):
- """
- ACCEPTS: [ 'box' | 'datalim' | 'box-forced']
- """
- if adjustable in ('box', 'datalim', 'box-forced'):
- if self in self._shared_x_axes or self in self._shared_y_axes:
- if adjustable == 'box':
- raise ValueError(
- 'adjustable must be "datalim" for shared axes')
- self._adjustable = adjustable
- else:
- raise ValueError('argument must be "box", or "datalim"')
-
- def get_anchor(self):
- return self._anchor
- def set_anchor(self, anchor):
- """
- *anchor*
-
- ===== ============
- value description
- ===== ============
- 'C' Center
- 'SW' bottom left
- 'S' bottom
- 'SE' bottom right
- 'E' right
- 'NE' top right
- 'N' top
- 'NW' top left
- 'W' left
- ===== ============
-
- """
- if anchor in mtransforms.Bbox.coefs.keys() or len(anchor) == 2:
- self._anchor = anchor
- else:
- raise ValueError('argument must be among %s' %
- ', '.join(mtransforms.BBox.coefs.keys()))
def get_data_ratio(self):
"""
@@ -1338,18 +1086,6 @@ def get_frame(self):
warnings.warn('use ax.patch instead', DeprecationWarning)
return self.patch
- def get_legend(self):
- """Return the legend.Legend instance, or None if no legend is defined"""
- return self.legend_
-
- def get_images(self):
- """return a list of Axes images contained by the Axes"""
- return cbook.silent_list('AxesImage', self.images)
-
- def get_lines(self):
- """Return a list of lines contained by the Axes"""
- return cbook.silent_list('Line2D', self.lines)
-
def get_xaxis(self):
"""Return the XAxis instance"""
return self.xaxis
@@ -1376,96 +1112,6 @@ def get_yticklines(self):
"""Get the ytick lines as a list of Line2D instances"""
return cbook.silent_list('Line2D ytickline', self.yaxis.get_ticklines())
- #### Adding and tracking artists
-
- def _sci(self, im):
- """
- helper for :func:`~matplotlib.pyplot.sci`;
- do not use elsewhere.
- """
- if isinstance(im, matplotlib.contour.ContourSet):
- if im.collections[0] not in self.collections:
- raise ValueError(
- "ContourSet must be in current Axes")
- elif im not in self.images and im not in self.collections:
- raise ValueError(
- "Argument must be an image, collection, or ContourSet in this Axes")
- self._current_image = im
-
- def _gci(self):
- """
- Helper for :func:`~matplotlib.pyplot.gci`;
- do not use elsewhere.
- """
- return self._current_image
-
- def has_data(self):
- """
- Return *True* if any artists have been added to axes.
-
- This should not be used to determine whether the *dataLim*
- need to be updated, and may not actually be useful for
- anything.
- """
- return (
- len(self.collections) +
- len(self.images) +
- len(self.lines) +
- len(self.patches))>0
-
- def add_artist(self, a):
- """
- Add any :class:`~matplotlib.artist.Artist` to the axes.
-
- Returns the artist.
- """
- a.set_axes(self)
- self.artists.append(a)
- self._set_artist_props(a)
- a.set_clip_path(self.patch)
- a._remove_method = lambda h: self.artists.remove(h)
- return a
-
- def add_collection(self, collection, autolim=True):
- """
- Add a :class:`~matplotlib.collections.Collection` instance
- to the axes.
-
- Returns the collection.
- """
- label = collection.get_label()
- if not label:
- collection.set_label('_collection%d'%len(self.collections))
- self.collections.append(collection)
- self._set_artist_props(collection)
-
- if collection.get_clip_path() is None:
- collection.set_clip_path(self.patch)
- if autolim:
- if collection._paths and len(collection._paths):
- self.update_datalim(collection.get_datalim(self.transData))
-
- collection._remove_method = lambda h: self.collections.remove(h)
- return collection
-
- def add_line(self, line):
- """
- Add a :class:`~matplotlib.lines.Line2D` to the list of plot
- lines
-
- Returns the line.
- """
- self._set_artist_props(line)
- if line.get_clip_path() is None:
- line.set_clip_path(self.patch)
-
- self._update_line_limits(line)
- if not line.get_label():
- line.set_label('_line%d'%len(self.lines))
- self.lines.append(line)
- line._remove_method = lambda h: self.lines.remove(h)
- return line
-
def _update_line_limits(self, line):
p = line.get_path()
if p.vertices.size > 0:
@@ -1474,24 +1120,6 @@ def _update_line_limits(self, line):
updatey=line.y_isdata)
self.ignore_existing_data_limits = False
- def add_patch(self, p):
- """
- Add a :class:`~matplotlib.patches.Patch` *p* to the list of
- axes patches; the clipbox will be set to the Axes clipping
- box. If the transform is not set, it will be set to
- :attr:`transData`.
-
- Returns the patch.
- """
-
- self._set_artist_props(p)
- if p.get_clip_path() is None:
- p.set_clip_path(self.patch)
- self._update_patch_limits(p)
- self.patches.append(p)
- p._remove_method = lambda h: self.patches.remove(h)
- return p
-
def _update_patch_limits(self, patch):
"""update the data limits for patch *p*"""
# hist can add zero height Rectangles, which is useful to keep
@@ -1513,52 +1141,6 @@ def _update_patch_limits(self, patch):
self.update_datalim(xys, updatex=patch.x_isdata,
updatey=patch.y_isdata)
-
- def add_table(self, tab):
- """
- Add a :class:`~matplotlib.tables.Table` instance to the
- list of axes tables
-
- Returns the table.
- """
- self._set_artist_props(tab)
- self.tables.append(tab)
- tab.set_clip_path(self.patch)
- tab._remove_method = lambda h: self.tables.remove(h)
- return tab
-
- def add_container(self, container):
- """
- Add a :class:`~matplotlib.container.Container` instance
- to the axes.
-
- Returns the collection.
- """
- label = container.get_label()
- if not label:
- container.set_label('_container%d'%len(self.containers))
- self.containers.append(container)
- container.set_remove_method(lambda h: self.containers.remove(container))
- return container
-
-
- def relim(self):
- """
- Recompute the data limits based on current artists.
-
- At present, :class:`~matplotlib.collections.Collection`
- instances are not supported.
- """
- # Collections are deliberately not supported (yet); see
- # the TODO note in artists.py.
- self.dataLim.ignore(True)
- self.ignore_existing_data_limits = True
- for line in self.lines:
- self._update_line_limits(line)
-
- for p in self.patches:
- self._update_patch_limits(p)
-
def update_datalim(self, xys, updatex=True, updatey=True):
"""Update the data lim bbox with seq of xy tups or equiv. 2-D array"""
# if no data is set currently, the bbox will ignore its
@@ -1583,13 +1165,6 @@ def update_datalim_numerix(self, x, y):
self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits)
self.ignore_existing_data_limits = False
- def update_datalim_bounds(self, bounds):
- """
- Update the datalim to include the given
- :class:`~matplotlib.transforms.Bbox` *bounds*
- """
- self.dataLim.set(mtransforms.Bbox.union([self.dataLim, bounds]))
-
def _process_unit_info(self, xdata=None, ydata=None, kwargs=None):
"""Look for unit *kwargs* and update the axis instances as necessary"""
@@ -1632,13 +1207,6 @@ def _process_unit_info(self, xdata=None, ydata=None, kwargs=None):
if ydata is not None:
self.yaxis.update_units(ydata)
- def in_axes(self, mouseevent):
- """
- Return *True* if the given *mouseevent* (in display coords)
- is in the Axes
- """
- return self.patch.contains(mouseevent)[0]
-
def get_autoscale_on(self):
"""
Get whether autoscaling is applied for both axes on plot commands
@@ -1767,18 +1335,6 @@ def margins(self, *args, **kw):
self.autoscale_view(tight=tight, scalex=scalex, scaley=scaley)
- def set_rasterization_zorder(self, z):
- """
- Set zorder value below which artists will be rasterized
- """
- self._rasterization_zorder = z
-
- def get_rasterization_zorder(self):
- """
- Get zorder value below which artists will be rasterized
- """
- return self._rasterization_zorder
-
def autoscale(self, enable=True, axis='both', tight=None):
"""
Convenience method for simple axis view autoscaling.
@@ -2000,26 +1556,6 @@ def draw(self, renderer=None, inframe=False):
renderer.close_group('axes')
self._cachedRenderer = renderer
- def draw_artist(self, a):
- """
- This method can only be used after an initial draw which
- caches the renderer. It is used to efficiently update Axes
- data (axis ticks, labels, etc are not updated)
- """
- assert self._cachedRenderer is not None
- a.draw(self._cachedRenderer)
-
- def redraw_in_frame(self):
- """
- This method can only be used after an initial draw which
- caches the renderer. It is used to efficiently update Axes
- data (axis ticks, labels, etc are not updated)
- """
- assert self._cachedRenderer is not None
- self.draw(self._cachedRenderer, inframe=True)
-
- def get_renderer_cache(self):
- return self._cachedRenderer
def __draw_animate(self):
# ignore for now; broken
@@ -2032,161 +1568,6 @@ def __draw_animate(self):
for tmp, a in dsu:
a.draw(renderer)
- #### Axes rectangle characteristics
-
- def get_frame_on(self):
- """
- Get whether the axes rectangle patch is drawn
- """
- return self._frameon
-
- def set_frame_on(self, b):
- """
- Set whether the axes rectangle patch is drawn
-
- ACCEPTS: [ *True* | *False* ]
- """
- self._frameon = b
-
- def get_axisbelow(self):
- """
- Get whether axis below is true or not
- """
- return self._axisbelow
-
- def set_axisbelow(self, b):
- """
- Set whether the axis ticks and gridlines are above or below most artists
-
- ACCEPTS: [ *True* | *False* ]
- """
- self._axisbelow = b
-
- @docstring.dedent_interpd
- def grid(self, b=None, which='major', axis='both', **kwargs):
- """
- Call signature::
-
- grid(self, b=None, which='major', axis='both', **kwargs)
-
- Set the axes grids on or off; *b* is a boolean. (For MATLAB
- compatibility, *b* may also be a string, 'on' or 'off'.)
-
- If *b* is *None* and ``len(kwargs)==0``, toggle the grid state. If
- *kwargs* are supplied, it is assumed that you want a grid and *b*
- is thus set to *True*.
-
- *which* can be 'major' (default), 'minor', or 'both' to control
- whether major tick grids, minor tick grids, or both are affected.
-
- *axis* can be 'both' (default), 'x', or 'y' to control which
- set of gridlines are drawn.
-
- *kwargs* are used to set the grid line properties, eg::
-
- ax.grid(color='r', linestyle='-', linewidth=2)
-
- Valid :class:`~matplotlib.lines.Line2D` kwargs are
-
- %(Line2D)s
-
- """
- if len(kwargs):
- b = True
- b = _string_to_bool(b)
-
- if axis == 'x' or axis == 'both':
- self.xaxis.grid(b, which=which, **kwargs)
- if axis == 'y' or axis == 'both':
- self.yaxis.grid(b, which=which, **kwargs)
-
- def ticklabel_format(self, **kwargs):
- """
- Convenience method for manipulating the ScalarFormatter
- used by default for linear axes.
-
- Optional keyword arguments:
-
- ============ =========================================
- Keyword Description
- ============ =========================================
- *style* [ 'sci' (or 'scientific') | 'plain' ]
- plain turns off scientific notation
- *scilimits* (m, n), pair of integers; if *style*
- is 'sci', scientific notation will
- be used for numbers outside the range
- 10`-m`:sup: to 10`n`:sup:.
- Use (0,0) to include all numbers.
- *useOffset* [True | False | offset]; if True,
- the offset will be calculated as needed;
- if False, no offset will be used; if a
- numeric offset is specified, it will be
- used.
- *axis* [ 'x' | 'y' | 'both' ]
- *useLocale* If True, format the number according to
- the current locale. This affects things
- such as the character used for the
- decimal separator. If False, use
- C-style (English) formatting. The
- default setting is controlled by the
- axes.formatter.use_locale rcparam.
- ============ =========================================
-
- Only the major ticks are affected.
- If the method is called when the
- :class:`~matplotlib.ticker.ScalarFormatter` is not the
- :class:`~matplotlib.ticker.Formatter` being used, an
- :exc:`AttributeError` will be raised.
-
- """
- style = kwargs.pop('style', '').lower()
- scilimits = kwargs.pop('scilimits', None)
- useOffset = kwargs.pop('useOffset', None)
- useLocale = kwargs.pop('useLocale', None)
- axis = kwargs.pop('axis', 'both').lower()
- if scilimits is not None:
- try:
- m, n = scilimits
- m+n+1 # check that both are numbers
- except (ValueError, TypeError):
- raise ValueError("scilimits must be a sequence of 2 integers")
- if style[:3] == 'sci':
- sb = True
- elif style in ['plain', 'comma']:
- sb = False
- if style == 'plain':
- cb = False
- else:
- cb = True
- raise NotImplementedError, "comma style remains to be added"
- elif style == '':
- sb = None
- else:
- raise ValueError, "%s is not a valid style value"
- try:
- if sb is not None:
- if axis == 'both' or axis == 'x':
- self.xaxis.major.formatter.set_scientific(sb)
- if axis == 'both' or axis == 'y':
- self.yaxis.major.formatter.set_scientific(sb)
- if scilimits is not None:
- if axis == 'both' or axis == 'x':
- self.xaxis.major.formatter.set_powerlimits(scilimits)
- if axis == 'both' or axis == 'y':
- self.yaxis.major.formatter.set_powerlimits(scilimits)
- if useOffset is not None:
- if axis == 'both' or axis == 'x':
- self.xaxis.major.formatter.set_useOffset(useOffset)
- if axis == 'both' or axis == 'y':
- self.yaxis.major.formatter.set_useOffset(useOffset)
- if useLocale is not None:
- if axis == 'both' or axis == 'x':
- self.xaxis.major.formatter.set_useLocale(useLocale)
- if axis == 'both' or axis == 'y':
- self.yaxis.major.formatter.set_useLocale(useLocale)
- except AttributeError:
- raise AttributeError(
- "This method only works with the ScalarFormatter.")
def locator_params(self, axis='both', tight=None, **kwargs):
"""
@@ -2304,29 +1685,6 @@ def tick_params(self, axis='both', **kwargs):
ykw.pop('labelbottom', None)
self.yaxis.set_tick_params(**ykw)
- def set_axis_off(self):
- """turn off the axis"""
- self.axison = False
-
- def set_axis_on(self):
- """turn on the axis"""
- self.axison = True
-
- def get_axis_bgcolor(self):
- """Return the axis background color"""
- return self._axisbg
-
- def set_axis_bgcolor(self, color):
- """
- set the axes background color
-
- ACCEPTS: any matplotlib color - see
- :func:`~matplotlib.pyplot.colors`
- """
-
- self._axisbg = color
- self.patch.set_facecolor(color)
-
### data limits, ticks, tick labels, and formatting
def invert_xaxis(self):
@@ -2826,87 +2184,6 @@ def format_coord(self, x, y):
ys = self.format_ydata(y)
return 'x=%s y=%s'%(xs,ys)
- #### Interactive manipulation
-
- def can_zoom(self):
- """
- Return *True* if this axes supports the zoom box button functionality.
- """
- return True
-
- def can_pan(self) :
- """
- Return *True* if this axes supports any pan/zoom button functionality.
- """
- return True
-
- def get_navigate(self):
- """
- Get whether the axes responds to navigation commands
- """
- return self._navigate
-
- def set_navigate(self, b):
- """
- Set whether the axes responds to navigation toolbar commands
-
- ACCEPTS: [ *True* | *False* ]
- """
- self._navigate = b
-
- def get_navigate_mode(self):
- """
- Get the navigation toolbar button status: 'PAN', 'ZOOM', or None
- """
- return self._navigate_mode
-
- def set_navigate_mode(self, b):
- """
- Set the navigation toolbar button status;
-
- .. warning::
- this is not a user-API function.
-
- """
- self._navigate_mode = b
-
- def start_pan(self, x, y, button):
- """
- Called when a pan operation has started.
-
- *x*, *y* are the mouse coordinates in display coords.
- button is the mouse button number:
-
- * 1: LEFT
- * 2: MIDDLE
- * 3: RIGHT
-
- .. note::
-
- Intended to be overridden by new projection types.
-
- """
- self._pan_start = cbook.Bunch(
- lim = self.viewLim.frozen(),
- trans = self.transData.frozen(),
- trans_inverse = self.transData.inverted().frozen(),
- bbox = self.bbox.frozen(),
- x = x,
- y = y
- )
-
- def end_pan(self):
- """
- Called when a pan operation completes (when the mouse button
- is up.)
-
- .. note::
-
- Intended to be overridden by new projection types.
-
- """
- del self._pan_start
-
def drag_pan(self, button, key, x, y):
"""
Called when the mouse moves during a pan operation.
@@ -2978,35 +2255,6 @@ def format_deltas(key, dx, dy):
self.set_xlim(*result.intervalx)
self.set_ylim(*result.intervaly)
- def get_cursor_props(self):
- """
- Return the cursor propertiess as a (*linewidth*, *color*)
- tuple, where *linewidth* is a float and *color* is an RGBA
- tuple
- """
- return self._cursorProps
-
- def set_cursor_props(self, *args):
- """
- Set the cursor property as::
-
- ax.set_cursor_props(linewidth, color)
-
- or::
-
- ax.set_cursor_props((linewidth, color))
-
- ACCEPTS: a (*float*, *color*) tuple
- """
- if len(args)==1:
- lw, c = args[0]
- elif len(args)==2:
- lw, c = args
- else:
- raise ValueError('args must be a (linewidth, color) tuple')
- c =mcolors.colorConverter.to_rgba(c)
- self._cursorProps = lw, c
-
def connect(self, s, func):
"""
Register observers to be notified when certain events occur. Register
@@ -3031,44 +2279,6 @@ def disconnect(self, cid):
raise DeprecationWarning('use the callbacks CallbackRegistry instance '
'instead')
- def get_children(self):
- """return a list of child artists"""
- children = []
- children.append(self.xaxis)
- children.append(self.yaxis)
- children.extend(self.lines)
- children.extend(self.patches)
- children.extend(self.texts)
- children.extend(self.tables)
- children.extend(self.artists)
- children.extend(self.images)
- if self.legend_ is not None:
- children.append(self.legend_)
- children.extend(self.collections)
- children.append(self.title)
- children.append(self.patch)
- children.extend(self.spines.itervalues())
- return children
-
- def contains(self,mouseevent):
- """
- Test whether the mouse event occured in the axes.
-
- Returns *True* / *False*, {}
- """
- if callable(self._contains): return self._contains(self,mouseevent)
-
- return self.patch.contains(mouseevent)
-
- def contains_point(self, point):
- """
- Returns *True* if the point (tuple of x,y) is inside the axes
- (the area defined by the its patch). A pixel coordinate is
- required.
-
- """
- return self.patch.contains_point(point, radius=1.0)
-
def pick(self, *args):
"""
Call signature::
@@ -3150,45 +2360,6 @@ def dist(a):
ds.sort()
return ds[0][1]
- #### Labelling
-
- def get_title(self):
- """
- Get the title text string.
- """
- return self.title.get_text()
-
- @docstring.dedent_interpd
- def set_title(self, label, fontdict=None, **kwargs):
- """
- Call signature::
-
- set_title(label, fontdict=None, **kwargs):
-
- Set the title for the axes.
-
- kwargs are Text properties:
- %(Text)s
-
- ACCEPTS: str
-
- .. seealso::
-
- :meth:`text`
- for information on how override and the optional args work
- """
- default = {
- 'fontsize':rcParams['axes.titlesize'],
- 'verticalalignment' : 'baseline',
- 'horizontalalignment' : 'center'
- }
-
- self.title.set_text(label)
- self.title.update(default)
- if fontdict is not None: self.title.update(fontdict)
- self.title.update(kwargs)
- return self.title
-
def get_xlabel(self):
"""
Get the xlabel text string.
@@ -4280,52 +3451,6 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none,
b = None
return lags, c, a, b
-
- def _get_legend_handles(self, legend_handler_map=None):
- "return artists that will be used as handles for legend"
- handles_original = self.lines + self.patches + \
- self.collections + self.containers
-
- # collections
- handler_map = mlegend.Legend.get_default_handler_map()
-
- if legend_handler_map is not None:
- handler_map = handler_map.copy()
- handler_map.update(legend_handler_map)
-
- handles = []
- for h in handles_original:
- if h.get_label() == "_nolegend_": #.startswith('_'):
- continue
- if mlegend.Legend.get_legend_handler(handler_map, h):
- handles.append(h)
-
- return handles
-
-
- def get_legend_handles_labels(self, legend_handler_map=None):
- """
- Return handles and labels for legend
-
- ``ax.legend()`` is equivalent to ::
-
- h, l = ax.get_legend_handles_labels()
- ax.legend(h, l)
-
- """
-
- handles = []
- labels = []
- for handle in self._get_legend_handles(legend_handler_map):
- label = handle.get_label()
- #if (label is not None and label != '' and not label.startswith('_')):
- if label and not label.startswith('_'):
- handles.append(handle)
- labels.append(label)
-
- return handles, labels
-
-
def legend(self, *args, **kwargs):
"""
Call signature::
@@ -4465,16 +3590,16 @@ def legend(self, *args, **kwargs):
implies a handlelength of 50 points. Values from rcParams
will be used if None.
- ================ ==================================================================
+ ============== ==================================================
Keyword Description
- ================ ==================================================================
+ ============== ==================================================
borderpad the fractional whitespace inside the legend border
labelspacing the vertical space between the legend entries
handlelength the length of the legend handles
handletextpad the pad between the legend handle and text
borderaxespad the pad between the axes and legend border
columnspacing the spacing between columns
- ================ ==================================================================
+ ============== ==================================================
.. Note:: Not all kinds of artist are supported by the legend command.
See LINK (FIXME) for details.
@@ -4489,20 +3614,20 @@ def legend(self, *args, **kwargs):
"""
- if len(args)==0:
+ if len(args) == 0:
handles, labels = self.get_legend_handles_labels()
if len(handles) == 0:
warnings.warn("No labeled objects found. "
"Use label='...' kwarg on individual plots.")
return None
- elif len(args)==1:
+ elif len(args) == 1:
# LABELS
labels = args[0]
handles = [h for h, label in zip(self._get_legend_handles(),
labels)]
- elif len(args)==2:
+ elif len(args) == 2:
if is_string_like(args[1]) or isinstance(args[1], int):
# LABELS, LOC
labels, loc = args
@@ -4513,18 +3638,13 @@ def legend(self, *args, **kwargs):
# LINES, LABELS
handles, labels = args
- elif len(args)==3:
+ elif len(args) == 3:
# LINES, LABELS, LOC
handles, labels, loc = args
kwargs['loc'] = loc
else:
raise TypeError('Invalid arguments to legend')
-
- # Why do we need to call "flatten" here? -JJL
- # handles = cbook.flatten(handles)
-
-
self.legend_ = mlegend.Legend(self, handles, labels, **kwargs)
return self.legend_
@@ -4712,7 +3832,7 @@ def make_iterable(x):
if len(height) == 1:
height *= nbars
else:
- raise ValueError, 'invalid orientation: %s' % orientation
+ raise ValueError('invalid orientation: %s' % orientation)
if len(linewidth) < nbars:
linewidth *= nbars
@@ -4735,16 +3855,18 @@ def make_iterable(x):
if len(edgecolor) < nbars:
edgecolor *= nbars
- # FIXME: convert the following to proper input validation
- # raising ValueError; don't use assert for this.
- assert len(left)==nbars, "incompatible sizes: argument 'left' must be length %d or scalar" % nbars
- assert len(height)==nbars, ("incompatible sizes: argument 'height' must be length %d or scalar" %
- nbars)
- assert len(width)==nbars, ("incompatible sizes: argument 'width' must be length %d or scalar" %
- nbars)
- assert len(bottom)==nbars, ("incompatible sizes: argument 'bottom' must be length %d or scalar" %
- nbars)
-
+ if len(left) != nbars:
+ raise ValueError("incompatible sizes: argument 'left' must be"
+ " length %d or scalar" % nbars)
+ if len(height) != nbars:
+ raise ValueError("incompatible sizes: argument 'height' must be"
+ " length %d or scalar" % nbars)
+ if len(width) != nbars:
+ raise ValueError("incompatible sizes: argument 'width' must be"
+ " length %d or scalar" % nbars)
+ if len(bottom) != nbars:
+ raise ValueError("incompatible sizes: argument 'bottom' must be"
+ " length %d or scalar" % nbars)
patches = []
# lets do some conversions now since some types cannot be
@@ -5079,6 +4201,7 @@ def pie(self, x, explode=None, labels=None, colors=None,
above, and *autotexts* is a list of
:class:`~matplotlib.text.Text` instances for the numeric
labels.
+
"""
self.set_frame_on(False)
@@ -5461,7 +4584,8 @@ def xywhere(xs, ys, mask):
self.autoscale_view()
self._hold = holdstate
- errorbar_container = ErrorbarContainer((l0, tuple(caplines), tuple(barcols)),
+ errorbar_container = ErrorbarContainer((l0, tuple(caplines),
+ tuple(barcols)),
has_xerr=(xerr is not None),
has_yerr=(yerr is not None),
label=label)
@@ -5541,6 +4665,7 @@ def boxplot(self, x, notch=0, sym='b+', vert=1, whis=1.5,
**Example:**
.. plot:: pyplots/boxplot_demo.py
+
"""
if not self._hold: self.cla()
holdStatus = self._hold
@@ -5745,7 +4870,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
faceted=True, verts=None,
**kwargs):
"""
- Call signatures::
+ Call signature::
scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None,
vmin=None, vmax=None, alpha=None, linewidths=None,
@@ -5826,6 +4951,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
A :class:`~matplotlib.collections.Collection` instance is
returned.
+
"""
if not self._hold: self.cla()
@@ -6248,9 +5374,9 @@ def hexbin(self, x, y, C = None, gridsize = 100, bins = None,
norm.autoscale(accum)
# Transform accum if needed
- if bins=='log':
+ if bins == 'log':
accum = np.log10(accum+1)
- elif bins!=None:
+ elif bins is not None:
if not iterable(bins):
minimum, maximum = min(accum), max(accum)
bins-=1 # one less edge than bins
@@ -6305,14 +5431,14 @@ def coarse_bin(x, y, coarse):
if not valid[i]: continue
- verts.append([(thismin, 0), (thismin, 0.05), (thismax, 0.05), (thismax, 0)])
+ verts.append([(thismin, 0), (thismin, 0.05),
+ (thismax, 0.05), (thismax, 0)])
values.append(val)
values = np.array(values)
trans = mtransforms.blended_transform_factory(
self.transData, self.transAxes)
-
hbar = mcoll.PolyCollection(verts, transform=trans, edgecolors='face')
hbar.set_array(values)
@@ -6332,8 +5458,10 @@ def coarse_bin(x, y, coarse):
thismax = coarse[i+1]
else:
thismax = thismin + np.diff(coarse)[-1]
- if not valid[i]: continue
- verts.append([(0, thismin), (0.0, thismax), (0.05, thismax), (0.05, thismin)])
+ if not valid[i]:
+ continue
+ verts.append([(0, thismin), (0.0, thismax),
+ (0.05, thismax), (0.05, thismin)])
values.append(val)
values = np.array(values)
@@ -6350,8 +5478,6 @@ def coarse_bin(x, y, coarse):
vbar.update(kwargs)
self.add_collection(vbar)
-
-
collection.hbar = hbar
collection.vbar = vbar
@@ -6383,6 +5509,7 @@ def arrow(self, x, y, dx, dy, **kwargs):
**Example:**
.. plot:: mpl_examples/pylab_examples/arrow_demo.py
+
"""
# Strip away units for the underlying patch since units
# do not make sense to most patch-like code
@@ -6434,6 +5561,7 @@ def barbs(self, *args, **kw):
**Example:**
.. plot:: mpl_examples/pylab_examples/barb_demo.py
+
"""
if not self._hold: self.cla()
b = mquiver.Barbs(self, *args, **kw)
@@ -6704,7 +5832,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, **kwargs):
x1slice = x1[ind0:ind1]
x2slice = x2[ind0:ind1]
- if not len(yslice):
+ if len(yslice) == 0:
continue
N = len(yslice)
@@ -6875,6 +6003,7 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,
# to tightly fit the image, regardless of dataLim.
im.set_extent(im.get_extent())
+ # TODO: We need an "add_image" method...
self.images.append(im)
im._remove_method = lambda h: self.images.remove(h)
@@ -6882,11 +6011,11 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,
def _pcolorargs(self, funcname, *args):
- if len(args)==1:
+ if len(args) == 1:
C = args[0]
numRows, numCols = C.shape
X, Y = np.meshgrid(np.arange(numCols+1), np.arange(numRows+1) )
- elif len(args)==3:
+ elif len(args) == 3:
X, Y, C = args
else:
raise TypeError(
@@ -6894,16 +6023,16 @@ def _pcolorargs(self, funcname, *args):
Nx = X.shape[-1]
Ny = Y.shape[0]
- if len(X.shape) <> 2 or X.shape[0] == 1:
+ if len(X.shape) != 2 or X.shape[0] == 1:
x = X.reshape(1,Nx)
X = x.repeat(Ny, axis=0)
- if len(Y.shape) <> 2 or Y.shape[1] == 1:
+ if len(Y.shape) != 2 or Y.shape[1] == 1:
y = Y.reshape(Ny, 1)
Y = y.repeat(Nx, axis=1)
if X.shape != Y.shape:
raise TypeError(
- 'Incompatible X, Y inputs to %s; see help(%s)' % (
- funcname, funcname))
+ 'Incompatible X, Y inputs to %s; see help(%s)' %
+ (funcname, funcname))
return X, Y, C
@docstring.dedent_interpd
@@ -7211,8 +6340,8 @@ def pcolormesh(self, *args, **kwargs):
# convert to one dimensional arrays
if shading != 'gouraud':
- C = ma.ravel(C[0:Ny-1, 0:Nx-1]) # data point in each cell is value at
- # lower left corner
+ C = ma.ravel(C[0:Ny-1, 0:Nx-1]) # data point in each cell is value
+ # at lower left corner
else:
C = C.ravel()
X = X.ravel()
@@ -7679,13 +6808,10 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
"""
if not self._hold: self.cla()
- # xrange becomes range after 2to3
+ # NOTE: the range keyword overwrites the built-in func range !!!
bin_range = range
range = __builtins__["range"]
- # NOTE: the range keyword overwrites the built-in func range !!!
- # needs to be fixed in numpy !!!
-
# Validate string inputs here so we don't have to clutter
# subsequent code.
if histtype not in ['bar', 'barstacked', 'step', 'stepfilled']:
@@ -7916,7 +7042,8 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
elif is_sequence_of_strings(label):
labels = list(label)
else:
- raise ValueError('invalid label: must be string or sequence of strings')
+ raise ValueError('invalid label: must be string or'
+ ' sequence of strings')
if len(labels) < nx:
labels += [None] * (nx - len(labels))
@@ -7955,51 +7082,61 @@ def hist2d(self, x, y, bins = 10, range=None, normed=False, weights=None,
"""
Call signature::
- hist2d(x, y, bins = None, range=None, weights=None, cmin=None, cmax=None **kwargs)
+ hist2d(x, y, bins = None, range=None, weights=None,
+ cmin=None, cmax=None **kwargs)
+
Make a 2d histogram plot of *x* versus *y*, where *x*,
*y* are 1-D sequences of the same length
The return value is (counts,xedges,yedges,Image)
Optional keyword arguments:
- *bins*: [None | int | [int, int] | array_like | [array, array]]
+ *bins*: [ *None* | int | [int, int] | array_like | [array, array] ]
The bin specification:
- If int, the number of bins for the two dimensions (nx=ny=bins).
- If [int, int], the number of bins in each dimension (nx, ny = bins).
- If array_like, the bin edges for the two dimensions (x_edges=y_edges=bins).
- If [array, array], the bin edges in each dimension (x_edges, y_edges = bins).
+ If int, the number of bins for the two dimensions
+ (nx=ny=bins).
+ If [int, int], the number of bins in each dimension
+ (nx, ny=bins).
+ If array_like, the bin edges for the two dimensions
+ (x_edges=y_edges=bins).
+ If [array, array], the bin edges in each dimension
+ (x_edges, y_edges=bins).
The default value is 10.
- *range*: [*None* | array_like shape(2,2)]
- The leftmost and rightmost edges of the bins along each dimension (if not specified
- explicitly in the bins parameters): [[xmin, xmax], [ymin, ymax]]. All values outside of
- this range will be considered outliers and not tallied in the histogram.
+ *range*: [ *None* | array_like shape(2,2) ]
+ The leftmost and rightmost edges of the bins along each dimension
+ (if not specified explicitly in the bins parameters):
+ [[xmin, xmax], [ymin, ymax]]. All values outside of this range
+ will be considered outliers and not tallied in the histogram.
- *normed*:[True|False]
+ *normed*: [ True | False ]
Normalize histogram.
The default value is False
- *weights*: [*None* | array]
+ *weights*: [ *None* | array ]
An array of values w_i weighing each sample (x_i, y_i).
- *cmin* : [None| scalar]
+ *cmin* : [ *None* | scalar ]
All bins that has count less than cmin will not be displayed
- and these count values in the return value count histogram will also be set to nan upon return
+ and these count values in the return value count histogram
+ will also be set to nan upon return
- *cmax* : [None| scalar]
- All bins that has count more than cmax will not be displayed (set to none before passing to imshow)
- and these count values in the return value count histogram will also be set to nan upon return
+ *cmax* : [ *None* | scalar ]
+ All bins that has count more than cmax will not be displayed
+ (set to *None* before passing to imshow) and these count values
+ in the returned histogram will also be set to *NaN* upon return.
- Remaining keyword arguments are passed directly to :meth:pcolorfast
+ Remaining keyword arguments are passed directly to :meth:`pcolorfast`
**Example:**
.. plot:: mpl_examples/pylab_examples/hist2d_demo.py
+
"""
- # xrange becomes range after 2to3
bin_range = range
range = __builtins__["range"]
+
h,xedges,yedges = np.histogram2d(x, y, bins=bins, range=bin_range,
normed=normed, weights=weights)
@@ -8076,7 +7213,8 @@ def psd(self, x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none,
vmin, vmax = self.viewLim.intervaly
intv = vmax-vmin
logi = int(np.log10(intv))
- if logi==0: logi=.1
+ if logi == 0:
+ logi=.1
step = 10*logi
#print vmin, vmax, step, intv, math.floor(vmin), math.ceil(vmax)+1
ticks = np.arange(math.floor(vmin), math.ceil(vmax)+1, step)
@@ -8131,6 +7269,7 @@ def csd(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none,
:meth:`psd`
For a description of the optional parameters.
+
"""
if not self._hold: self.cla()
pxy, freqs = mlab.csd(x, y, NFFT, Fs, detrend, window, noverlap,
@@ -8445,12 +7584,6 @@ def matshow(self, Z, **kwargs):
integer=True))
return im
- def get_default_bbox_extra_artists(self):
- bbox_extra_artists = [t for t in self.texts if t.get_visible()]
- if self.legend_:
- bbox_extra_artists.append(self.legend_)
- return bbox_extra_artists
-
def get_tightbbox(self, renderer, call_axes_locator=True):
"""
@@ -8462,6 +7595,7 @@ def get_tightbbox(self, renderer, call_axes_locator=True):
bounding box. ``call_axes_locator==False`` can be used if the
caller is only intereted in the relative size of the tightbbox
compared to the axes bbox.
+
"""
artists = []
@@ -8488,24 +7622,11 @@ def get_tightbbox(self, renderer, call_axes_locator=True):
bb_yaxis = self.yaxis.get_tightbbox(renderer)
if bb_yaxis: bb.append(bb_yaxis)
- _bbox = mtransforms.Bbox.union([b for b in bb if b.width!=0 or b.height!=0])
+ _bbox = mtransforms.Bbox.union([b for b in bb if
+ b.width!=0 or b.height!=0])
return _bbox
- def minorticks_on(self):
- 'Add autoscaling minor ticks to the axes.'
- for ax in (self.xaxis, self.yaxis):
- if ax.get_scale() == 'log':
- s = ax._scale
- ax.set_minor_locator(mticker.LogLocator(s.base, s.subs))
- else:
- ax.set_minor_locator(mticker.AutoMinorLocator())
-
- def minorticks_off(self):
- """Remove minor ticks from the axes."""
- self.xaxis.set_minor_locator(mticker.NullLocator())
- self.yaxis.set_minor_locator(mticker.NullLocator())
-
def tricontour(self, *args, **kwargs):
return mtri.tricontour(self, *args, **kwargs)
tricontour.__doc__ = mtri.TriContourSet.tricontour_doc
1,039 lib/matplotlib/axesbase.py
View
@@ -0,0 +1,1039 @@
+
+# We need this future import because we unfortunately have
+# a module name collision with the standard module called "collections".
+from __future__ import absolute_import
+from collections import defaultdict, OrderedDict
Phil Elson Collaborator
pelson added a note

can you not just do import matplotlib.collections?

Benjamin Root Collaborator

You are getting mixed up. I want defaultdict and OrderedDict from the standard library's "collections" module, not matplotlib's. But because axesbase.py is in the same directory as matplotlib's collections.py, pre-py3k import rules will accidentially grab matplotlib's collections module. It is for this exact reason that implicit relative imports is being deprecated.

Phil Elson Collaborator
pelson added a note

Ah yes. Ignore me, I misread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+import matplotlib.artist as martist
+import matplotlib.colors as mcolors
+import matplotlib.contour as mcontour
+import matplotlib.legend as mlegend
+import matplotlib.lines as mlines
+import matplotlib.text as mtext
+import matplotlib.transforms as mtransforms
+from matplotlib import cbook
+from matplotlib import docstring
+from matplotlib import rcParams
+
+is_string_like = cbook.is_string_like
+
+def _string_to_bool(s):
+ if not is_string_like(s):
+ return s
+ if s == 'on':
+ return True
+ if s == 'off':
+ return False
+ raise ValueError("string argument must be either 'on' or 'off'")
+
+
+
+
+class AxesBase(martist.Artist):
+ """
+ This class serves to logically segregate some of the methods of the
+ :class:`~matplotlib.axes.Axes` class into their own base class.
+ These methods are largely generalized maintenance operations and
+ other book-keeping actions. Ultimately, the objective is for the
+ Axes class will contain only pure plotting methods.
+
+ It is still intended for developers and extension writers to continue
+ subclassing the Axes class, and no existing code will be broken by
+ this migration effort.
+
+ """
+
+ _shared_axes = defaultdict(cbook.Grouper)
+ _axis_classes = {}
+ _axis_locs = {}
+
+ def __init__(self, fig, rect, axis_names,
Phil Elson Collaborator
pelson added a note

Would need a docstring.

Benjamin Root Collaborator

Yes, I plan on moving over a significant portion of Axes's docstring over to here, and have Axes's docstring be built partly from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ axisbg=None, # defaults to rc axes.facecolor
+ frameon=True,
+ label='',
+ share=None,
+ scale=None,
+ axes_locator=None):
+ martist.Artist.__init__(self)
+ self._position = (rect if isinstance(rect, mtransforms.Bbox) else
+ mtransforms.Bbox.from_bounds(*rect))
+
+ self._share = share or dict([(name, None) for name in axis_names])
+ self._scale = scale or dict([(name, None) for name in axis_names])
+ self._axis_transforms = dict([(name, None) for name in axis_names])
+ self._axis_objs = OrderedDict([(name, None) for name in axis_names])
+
+ self._originalPosition = self._position.frozen()
+ self.set_axes(self)
+ self.set_aspect('auto')
+ self._adjustable = 'box'
+ self.set_anchor('C')
+
+ # TODO: Backwards compatibility shim
+ self._sharex = self._share.get('x', None)
+ self._sharey = self._share.get('y', None)
+
+ for axis_name in axis_names :
+ shared = self._share.get(axis_name, None)
+ if shared is not None:
+ self._shared_axes[axis_name].join(self, shared)
+ # 'box' and 'datalim' are equivalent now.
+ if shared._adjustable == 'box':
+ shared._adjustable = 'datalim'
+ self._adjustable = 'datalim'
+
+ self.set_label(label)
+ self.set_figure(fig)
+ self.set_axes_locator(axes_locator)
+
+ self.spines = self._gen_axes_spines()
+
+ def ishold(self):
+ """return the HOLD status of the axes"""
+ return self._hold
+
+ def hold(self, b=None):
+ """
+ Call signature::
+
+ hold(b=None)
+
+ Set the hold state. If *hold* is *None* (default), toggle the
+ *hold* state. Else set the *hold* state to boolean value *b*.
+
+ Examples::
+
+ # toggle hold
+ hold()
+
+ # turn hold on
+ hold(True)
+
+ # turn hold off
+ hold(False)
+
+
+ When hold is *True*, subsequent plot commands will be added to
+ the current axes. When hold is *False*, the current axes and
+ figure will be cleared on the next plot command
+
+ """
+ if b is None:
+ self._hold = not self._hold
+ else:
+ self._hold = b
+
+ def get_aspect(self):
+ return self._aspect
+
+ def set_aspect(self, aspect, adjustable=None, anchor=None):
+ """
+ *aspect*
+
+ ======== ================================================
+ value description
+ ======== ================================================
+ 'auto' automatic; fill position rectangle with data
+ 'normal' same as 'auto'; deprecated
+ 'equal' same scaling from data to plot units for x and y
+ num a circle will be stretched such that the height
+ is num times the width. aspect=1 is the same as
+ aspect='equal'.
+ ======== ================================================
+
+ *adjustable*
+
+ ============ =====================================
+ value description
+ ============ =====================================
+ 'box' change physical size of axes
+ 'datalim' change xlim or ylim
+ 'box-forced' same as 'box', but axes can be shared
+ ============ =====================================
+
+ 'box' does not allow axes sharing, as this can cause
+ unintended side effect. For cases when sharing axes is
+ fine, use 'box-forced'.
+
+ *anchor*
+
+ ===== =====================
+ value description
+ ===== =====================
+ 'C' centered
+ 'SW' lower left corner
+ 'S' middle of bottom edge
+ 'SE' lower right corner
+ etc.
+ ===== =====================
+
+ """
+ if aspect in ('normal', 'auto'):
+ self._aspect = 'auto'
+ elif aspect == 'equal':
+ self._aspect = 'equal'
+ else:
+ self._aspect = float(aspect) # raise ValueError if necessary
+
+ if adjustable is not None:
+ self.set_adjustable(adjustable)
+ if anchor is not None:
+ self.set_anchor(anchor)
+
+ def get_adjustable(self):
+ return self._adjustable
+
+ def set_adjustable(self, adjustable):
+ """
+ ACCEPTS: [ 'box' | 'datalim' | 'box-forced']
+ """
+ if adjustable in ('box', 'datalim', 'box-forced'):
+ if any((self in shared) for shared in self._shared_axes.values()):
+ if adjustable == 'box':
+ raise ValueError(
+ 'adjustable must be "datalim" for shared axes')
+ self._adjustable = adjustable
+ else:
+ raise ValueError('argument must be "box", or "datalim"')
+
+ def get_anchor(self):
+ return self._anchor
+
+ def set_anchor(self, anchor):
+ """
+ *anchor*
+
+ ===== ============
+ Value Description
+ ===== ============
+ 'C' center
+ 'SW' bottom left
+ 'S' bottom
+ 'SE' bottom right
+ 'E' right
+ 'NE' top right
+ 'N' top
+ 'NW' top left
+ 'W' left
+ ===== ============
+
+ """
+ if anchor in mtransforms.Bbox.coefs.keys() or len(anchor) == 2:
+ self._anchor = anchor
+ else:
+ raise ValueError('argument must be among %s' %
+ ', '.join(mtransforms.BBox.coefs.keys()))
+
+ def set_figure(self, fig):
+ """
+ Set the :class:`~matplotlib.axes.Axes`'s figure.
+
+ Accepts a :class:`~matplotlib.figure.Figure` instance.
+
+ """
+ martist.Artist.set_figure(self, fig)
+
+ self.bbox = mtransforms.TransformedBbox(self._position, fig.transFigure)
+ #these will be updated later as data is added
+ self.dataLim = mtransforms.Bbox.unit()
+ self.viewLim = mtransforms.Bbox.unit()
+ self.transScale = mtransforms.TransformWrapper(
+ mtransforms.IdentityTransform())
+
+ self._set_lim_and_transforms()
+
+ def _set_lim_and_transforms(self):
+ """
+ set the *dataLim* and *viewLim*
+ :class:`~matplotlib.transforms.Bbox` attributes and the
+ *transScale*, *transData*, *transLimits* and *transAxes*
+ transformations.
+
+ .. note::
+
+ This method is primarily used by rectilinear projections
+ of the :class:`~matplotlib.axes.Axes` class, and is meant
+ to be overridden by new kinds of projection axes that need
+ different transformations and limits. (See
+ :class:`~matplotlib.projections.polar.PolarAxes` for an
+ example).
+
+ """
+ raise NotImplementedError("The %s class needs to define"
+ " _set_lim_and_transforms")
+
+ def _get_axis_transform(self, which, axis_name):
+ """
+ Get the transformation used for drawing axis labels, ticks
+ and gridlines. This is meant as an internal method for the migration
+ process.
+
+ Values for "which": 'grid', 'tick1', 'tick2'
+ """
+ spine_locs = self._axis_locs[axis_name]
+ if which=='grid':
+ return self._axis_transforms[axis_name]
+ elif which=='tick1':
+ return self.spines[spine_locs[0]].get_spine_transform()
+ elif which=='tick2':
+ return self.spines[spine_locs[1]].get_spine_transform()
+ else:
+ raise ValueError('unknown value for "which"')
+
+ def get_position(self, original=False):
+ 'Return the a copy of the axes rectangle as a Bbox'
+ if original:
+ return self._originalPosition.frozen()
+ else:
+ return self._position.frozen()
+
+
+ def set_position(self, pos, which='both'):
+ """
+ Set the axes position with::
+
+ pos = [left, bottom, width, height]
+
+ in relative 0,1 coords, or *pos* can be a
+ :class:`~matplotlib.transforms.Bbox`
+
+ There are two position variables: one which is ultimately
+ used, but which may be modified by :meth:`apply_aspect`, and a
+ second which is the starting point for :meth:`apply_aspect`.
+
+
+ Optional keyword arguments:
+ *which*
+
+ ========== ====================
+ value description
+ ========== ====================
+ 'active' to change the first
+ 'original' to change the second
+ 'both' to change both
+ ========== ====================
+
+ """
+ if not isinstance(pos, mtransforms.BboxBase):
+ pos = mtransforms.Bbox.from_bounds(*pos)
+ if which in ('both', 'active'):
+ self._position.set(pos)
+ if which in ('both', 'original'):
+ self._originalPosition.set(pos)
+
+ def reset_position(self):
+ """Make the original position the active position"""
+ pos = self.get_position(original=True)
+ self.set_position(pos, which='active')
+
+ def set_axes_locator(self, locator):
+ """
+ Set axes_locator
+
+ ACCEPT : a callable object which takes an axes instance and
+ renderer and returns a bbox.
+ """
+ self._axes_locator = locator
+
+ def get_axes_locator(self):
+ """
+ Return axes_locator
+ """
+ return self._axes_locator
+
+ def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'):
+ """
+ Returns a dict whose keys are spine names and values are
+ Line2D or Patch instances. Each element is used to draw a
+ spine of the axes.
+
+ In the standard axes, this is a single line segment, but in
+ other projections it may not be.
+
+ .. note::
+
+ Intended to be overridden by new projection types.
+
+ """
+ raise NotImplementedError("The subclass %s must define"
+ " _gen_axes_spines()" % type(self))
+ def _init_axis(self):
+ try:
+ for name in self._axis_objs:
+ self._axis_objs[name] = self._axis_classes[name](self)
+ for loc in self._axis_locs[name]:
+ self.spines[loc].register_axis(self._axis_objs[name])
+ except KeyError:
+ # TODO: Add information to this exception to make it clearer what
+ # is wrong.
+ raise
+
+ self._update_transScale()
+
+ def _update_transScale(self):
+ self.transScale.set(
+ mtransforms.blended_transform_factory(
+ *[self._axis_objs[name].get_transform() for name in
+ self._axis_objs]))
+ if hasattr(self, "lines"):
+ for line in self.lines:
+ try:
+ line._transformed_path.invalidate()
+ except AttributeError:
+ pass
+
+ def _set_artist_props(self, a):
+ """set the boilerplate props for artists added to axes"""
+ a.set_figure(self.figure)
+ if not a.is_transform_set():
+ a.set_transform(self.transData)
+
+ a.set_axes(self)
+
+ def get_legend(self):
+ """Return the legend.Legend instance, or None if no legend is defined"""
+ return self.legend_
+
+ def get_images(self):
+ """return a list of Axes images contained by the Axes"""
+ return cbook.silent_list('AxesImage', self.images)
+
+ def get_lines(self):
+ """Return a list of lines contained by the Axes"""
+ return cbook.silent_list('Line2D', self.lines)
+
+ #### Adding and tracking artists
+
+ def _sci(self, im):
+ """
+ helper for :func:`~matplotlib.pyplot.sci`;
+ do not use elsewhere.
+ """
+ if isinstance(im, mcontour.ContourSet):
+ if im.collections[0] not in self.collections:
+ raise ValueError(
+ "ContourSet must be in current Axes")
+ elif im not in self.images and im not in self.collections:
+ raise ValueError(
+ "Argument must be an image, collection, or ContourSet in this Axes")
+ self._current_image = im
+
+ def _gci(self):
+ """
+ Helper for :func:`~matplotlib.pyplot.gci`;
+ do not use elsewhere.
+ """
+ return self._current_image
+
+ def has_data(self):
+ """
+ Return *True* if any artists have been added to axes.
+
+ This should not be used to determine whether the *dataLim*
+ need to be updated, and may not actually be useful for
+ anything.
+ """
+ return (
+ len(self.collections) +
+ len(self.images) +
+ len(self.lines) +
+ len(self.patches))>0
+
+ def add_artist(self, a):
+ """
+ Add any :class:`~matplotlib.artist.Artist` to the axes.
+
+ Returns the artist.
+ """
+ a.set_axes(self)
+ self.artists.append(a)
+ self._set_artist_props(a)
+ a.set_clip_path(self.patch)
+ a._remove_method = lambda h: self.artists.remove(h)
+ return a
+
+ def add_collection(self, collection, autolim=True):
+ """
+ Add a :class:`~matplotlib.collections.Collection` instance
+ to the axes.
+
+ Returns the collection.
+ """
+ label = collection.get_label()
+ if not label:
+ collection.set_label('_collection%d'%len(self.collections))
+ self.collections.append(collection)
+ self._set_artist_props(collection)
+
+ if collection.get_clip_path() is None:
+ collection.set_clip_path(self.patch)
+ if autolim:
+ if collection._paths and len(collection._paths):
+ self.update_datalim(collection.get_datalim(self.transData))
+
+ collection._remove_method = lambda h: self.collections.remove(h)
+ return collection
+
+ def add_line(self, line):
+ """
+ Add a :class:`~matplotlib.lines.Line2D` to the list of plot
+ lines
+
+ Returns the line.
+ """
+ self._set_artist_props(line)
+ if line.get_clip_path() is None:
+ line.set_clip_path(self.patch)
+
+ self._update_line_limits(line)
+ if not line.get_label():
+ line.set_label('_line%d' % len(self.lines))
+ self.lines.append(line)
+ line._remove_method = lambda h: self.lines.remove(h)
+ return line
+
+ def add_patch(self, p):
+ """
+ Add a :class:`~matplotlib.patches.Patch` *p* to the list of
+ axes patches; the clipbox will be set to the Axes clipping
+ box. If the transform is not set, it will be set to
+ :attr:`transData`.
+
+ Returns the patch.
+ """
+
+ self._set_artist_props(p)
+ if p.get_clip_path() is None:
+ p.set_clip_path(self.patch)
+ self._update_patch_limits(p)
+ self.patches.append(p)
+ p._remove_method = lambda h: self.patches.remove(h)
+ return p
+
+ def add_table(self, tab):
+ """
+ Add a :class:`~matplotlib.tables.Table` instance to the
+ list of axes tables
+
+ Returns the table.
+ """
+ self._set_artist_props(tab)
+ self.tables.append(tab)
+ tab.set_clip_path(self.patch)
+ tab._remove_method = lambda h: self.tables.remove(h)
+ return tab
+
+ def add_container(self, container):
+ """
+ Add a :class:`~matplotlib.container.Container` instance
+ to the axes.
+
+ Returns the collection.
+ """
+ label = container.get_label()
+ if not label:
+ container.set_label('_container%d'%len(self.containers))
+ self.containers.append(container)
+ container.set_remove_method(lambda h: self.containers.remove(container))
+ return container
+
+ def relim(self):
+ """
+ Recompute the data limits based on current artists.
+
+ At present, :class:`~matplotlib.collections.Collection`
+ instances are not supported.
+
+ """
+ # Collections are deliberately not supported (yet); see
+ # the TODO note in artists.py.
+ self.dataLim.ignore(True)
+ self.ignore_existing_data_limits = True
+ for line in self.lines:
+ self._update_line_limits(line)
+
+ for p in self.patches:
+ self._update_patch_limits(p)
+
+ def update_datalim_bounds(self, bounds):
+ """
+ Update the datalim to include the given
+ :class:`~matplotlib.transforms.Bbox` *bounds*
+ """
+ self.dataLim.set(mtransforms.Bbox.union([self.dataLim, bounds]))
+
+ def in_axes(self, mouseevent):
+ """
+ Return *True* if the given *mouseevent* (in display coords)
+ is in the Axes
+ """
+ return self.patch.contains(mouseevent)[0]
+
+ def set_rasterization_zorder(self, z):
+ """
+ Set zorder value below which artists will be rasterized
+ """
+ self._rasterization_zorder = z
+
+ def get_rasterization_zorder(self):
+ """
+ Get zorder value below which artists will be rasterized
+ """
+ return self._rasterization_zorder
+
+ def draw_artist(self, a):
+ """
+ This method can only be used after an initial draw which
+ caches the renderer. It is used to efficiently update Axes
+ data (axis ticks, labels, etc are not updated)
+
+ """
+ assert self._cachedRenderer is not None
+ a.draw(self._cachedRenderer)
+
+ def redraw_in_frame(self):
+ """
+ This method can only be used after an initial draw which