From 37e3851ec451a4d63ba03bfde84c070999b6ab3f Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Wed, 6 Jun 2012 10:04:56 +0100 Subject: [PATCH 1/6] Squashed class_projections from #694 --- doc/api/api_changes.rst | 11 ++ doc/devel/add_new_projection.rst | 26 +++- lib/matplotlib/axes.py | 8 +- lib/matplotlib/figure.py | 160 ++++++++++++++--------- lib/matplotlib/projections/__init__.py | 48 +++++++ lib/matplotlib/tests/test_tightlayout.py | 3 + 6 files changed, 191 insertions(+), 65 deletions(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 8154b767a393..d944ad4af621 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -22,6 +22,17 @@ Changes in 1.2.x now set the radius of the pie; setting the *radius* to 'None' (the default value), will result in a pie with a radius of 1 as before. +* Use of :func:`matplotlib.projections.projection_factory` is now deprecated + in favour of axes class identification using + :func:`matplotlib.projections.process_projection_requirements` followed by + direct axes class invocation (at the time of writing, this is done by + :meth:`matplotlib.figure.Figure.add_axes`, + :meth:`matplotlib.figure.Figure.add_subplot` and + :meth:`matplotlib.figure.Figure.gca`. + This change means that third party objects can expose themselves as + matplotlib axes by providing a ``_as_mpl_axes`` method (see + :ref:`adding-new-scales` for more detail). + Changes in 1.1.x ================ diff --git a/doc/devel/add_new_projection.rst b/doc/devel/add_new_projection.rst index 5efbead7fc40..a823bd9baf72 100644 --- a/doc/devel/add_new_projection.rst +++ b/doc/devel/add_new_projection.rst @@ -71,8 +71,9 @@ in :mod:`matplotlib.scale` that may be used as starting points. Creating a new projection ========================= -Adding a new projection consists of defining a subclass of -:class:`matplotlib.axes.Axes`, that includes the following elements: +Adding a new projection consists of defining a projection axes which +subclasses :class:`matplotlib.axes.Axes` and includes the following +elements: - A transformation from data coordinates into display coordinates. @@ -101,8 +102,25 @@ Adding a new projection consists of defining a subclass of - Any additional methods for additional convenience or features. -Once the class is defined, it must be registered with matplotlib -so that the user can select it. +Once the projection axes is defined, it can be used in one of two ways: + + - By defining the class attribute ``NAME``, the projection axes can be + registered with :func:`matplotlib.projections.register_projection` + and subsequently simply invoked by name:: + + plt.axes(projection=NAME) + + - For more complex, parameterisable projections, a generic "projection" + object may be defined which includes the method ``_as_mpl_axes``. + ``_as_mpl_axes`` should take no arguments and return the projection's + axes subclass and a dictionary of additional arguments to pass to the + subclass' ``__init__`` method. Subsequently a parameterised projection + can be initialised with:: + + plt.axes(projection=MyProjection(param1=param1_value)) + + where MyProjection is an object which implements a ``_as_mpl_axes`` method. + A full-fledged and heavily annotated example is in :file:`examples/api/custom_projection_example.py`. The polar plot diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index 42208cd1e71b..a6aa19f5332a 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -3514,7 +3514,9 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): * draw a gray rectangle from *y* = 0.25-0.75 that spans the horizontal extent of the axes:: - >>> axhspan(0.25, 0.75, facecolor='0.5', alpha=0.5) + # draw a gray rectangle from *y* = 0.25-0.75 that spans the + # horizontal extent of the axes + axhspan(0.25, 0.75, facecolor='0.5', alpha=0.5) Valid kwargs are :class:`~matplotlib.patches.Polygon` properties: @@ -3570,7 +3572,9 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): * draw a vertical green translucent rectangle from x=1.25 to 1.55 that spans the yrange of the axes:: - >>> axvspan(1.25, 1.55, facecolor='g', alpha=0.5) + # draw a vertical green translucent rectangle from x=1.25 to 1.55 + # that spans the yrange of the axes + axvspan(1.25, 1.55, facecolor='g', alpha=0.5) Valid kwargs are :class:`~matplotlib.patches.Polygon` properties: diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 9797b3633da4..fe233ec7bf25 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -27,8 +27,8 @@ from legend import Legend from transforms import Affine2D, Bbox, BboxTransformTo, TransformedBbox -from projections import projection_factory, get_projection_names, \ - get_projection_class +from projections import get_projection_names, get_projection_class, \ + process_projection_requirements from matplotlib.blocking_input import BlockingMouseInput, BlockingKeyMouseInput import matplotlib.cbook as cbook @@ -41,11 +41,18 @@ class AxesStack(Stack): """ - Specialization of the Stack to handle all - tracking of Axes in a Figure. This requires storing - key, (ind, axes) pairs. The key is based on the args and kwargs - used in generating the Axes. ind is a serial number for tracking - the order in which axes were added. + Specialization of the Stack to handle all tracking of Axes in a Figure. + This stack stores ``key, (ind, axes)`` pairs, where: + + * **key** should be a hash of the args and kwargs + used in generating the Axes. + * **ind** is a serial number for tracking the order + in which axes were added. + + The AxesStack is a callable, where ``ax_stack()`` returns + the current axes. Alternatively the :meth:`current_key_axes` will + return the current key and associated axes. + """ def __init__(self): Stack.__init__(self) @@ -74,9 +81,14 @@ def _entry_from_axes(self, e): return (k, (ind, e)) def remove(self, a): + """Remove the axes from the stack.""" Stack.remove(self, self._entry_from_axes(a)) def bubble(self, a): + """ + Move the given axes, which must already exist in the + stack, to the top. + """ return Stack.bubble(self, self._entry_from_axes(a)) def add(self, key, a): @@ -107,11 +119,21 @@ def add(self, key, a): self._ind += 1 return Stack.push(self, (key, (self._ind, a))) - def __call__(self): + def current_key_axes(self): + """ + Return a tuple of ``(key, axes)`` for the active axes. + + If no axes exists on the stack, then returns ``(None, None)``. + + """ if not len(self._elements): - return self._default + return self._default, self._default else: - return self._elements[self._pos][1][1] + key, (index, axes) = self._elements[self._pos] + return key, axes + + def __call__(self): + return self.current_key_axes()[1] def __contains__(self, a): return a in self.as_list() @@ -692,6 +714,8 @@ def add_axes(self, *args, **kwargs): """ if not len(args): return + # shortcut the projection "key" modifications later on, if an axes + # with the exact args/kwargs exists, return it immediately. key = self._make_key(*args, **kwargs) ax = self._axstack.get(key) if ax is not None: @@ -703,17 +727,18 @@ def add_axes(self, *args, **kwargs): assert(a.get_figure() is self) else: rect = args[0] - ispolar = kwargs.pop('polar', False) - projection = kwargs.pop('projection', None) - if ispolar: - if projection is not None and projection != 'polar': - raise ValueError( - "polar=True, yet projection='%s'. " + - "Only one of these arguments should be supplied." % - projection) - projection = 'polar' - - a = projection_factory(projection, self, rect, **kwargs) + projection_class, kwargs, key = \ + process_projection_requirements(self, *args, **kwargs) + + # check that an axes of this type doesn't already exist, if it + # does, set it as active and return it + ax = self._axstack.get(key) + if ax is not None and isinstance(ax, projection_class): + self.sca(ax) + return ax + + # create the new axes using the axes class given + a = projection_class(self, rect, **kwargs) self._axstack.add(key, a) self.sca(a) @@ -725,10 +750,18 @@ def add_subplot(self, *args, **kwargs): Add a subplot. Examples:: fig.add_subplot(111) - fig.add_subplot(1,1,1) # equivalent but more general - fig.add_subplot(212, axisbg='r') # add subplot with red background - fig.add_subplot(111, polar=True) # add a polar subplot - fig.add_subplot(sub) # add Subplot instance sub + + # equivalent but more general + fig.add_subplot(1,1,1) + + # add subplot with red background + fig.add_subplot(212, axisbg='r') + + # add a polar subplot + fig.add_subplot(111, projection='polar') + + # add Subplot instance sub + fig.add_subplot(sub) *kwargs* are legal :class:`~matplotlib.axes.Axes` kwargs plus *projection*, which chooses a projection type for the axes. @@ -755,39 +788,32 @@ def add_subplot(self, *args, **kwargs): args = tuple([int(c) for c in str(args[0])]) if isinstance(args[0], SubplotBase): + a = args[0] assert(a.get_figure() is self) - key = self._make_key(*args, **kwargs) + key = self._make_key(*args[1:], **kwargs) else: - kwargs = kwargs.copy() - ispolar = kwargs.pop('polar', False) - projection = kwargs.pop('projection', None) - if ispolar: - if projection is not None and projection != 'polar': - raise ValueError( - "polar=True, yet projection='%s'. " + - "Only one of these arguments should be supplied." % - projection) - projection = 'polar' - - projection_class = get_projection_class(projection) - - # Remake the key without projection kwargs: - key = self._make_key(*args, **kwargs) + projection_class, kwargs, key = \ + process_projection_requirements(self, *args, **kwargs) + + # try to find the axes with this key in the stack ax = self._axstack.get(key) + if ax is not None: if isinstance(ax, projection_class): + # the axes already existed, so set it as active & return self.sca(ax) return ax else: - self._axstack.remove(ax) # Undocumented convenience behavior: # subplot(111); subplot(111, projection='polar') # will replace the first with the second. # Without this, add_subplot would be simpler and # more similar to add_axes. + self._axstack.remove(ax) a = subplot_class_factory(projection_class)(self, *args, **kwargs) + self._axstack.add(key, a) self.sca(a) return a @@ -1046,24 +1072,40 @@ def gca(self, **kwargs): """ Return the current axes, creating one if necessary - The following kwargs are supported + The following kwargs are supported for ensuring the returned axes + adheres to the given projection etc., and for axes creation if + the active axes does not exist: %(Axes)s + + .. note:: + When specifying kwargs to ``gca`` to find the pre-created active + axes, they should be equivalent in every way to the kwargs which + were used in its creation. + """ - ax = self._axstack() - if ax is not None: - ispolar = kwargs.get('polar', False) - projection = kwargs.get('projection', None) - if ispolar: - if projection is not None and projection != 'polar': - raise ValueError( - "polar=True, yet projection='%s'. " + - "Only one of these arguments should be supplied." % - projection) - projection = 'polar' - - projection_class = get_projection_class(projection) - if isinstance(ax, projection_class): - return ax + ckey, cax = self._axstack.current_key_axes() + # if there exists an axes on the stack see if it maches + # the desired axes configuration + if cax is not None: + + # if no kwargs are given just return the current axes + # this is a convenience for gca() on axes such as polar etc. + if not kwargs: + return cax + + # if the user has specified particular projection detail + # then build up a key which can represent this + else: + # we don't want to modify the original kwargs + # so take a copy so that we can do what we like to it + kwargs_copy = kwargs.copy() + projection_class, _, key = \ + process_projection_requirements(self, **kwargs_copy) + # if the cax matches this key then return the axes, otherwise + # continue and a new axes will be created + if key == ckey and isinstance(cax, projection_class): + return cax + return self.add_subplot(111, **kwargs) def sca(self, a): @@ -1094,7 +1136,7 @@ def savefig(self, *args, **kwargs): savefig(fname, dpi=None, facecolor='w', edgecolor='w', orientation='portrait', papertype=None, format=None, - transparent=False, bbox_inches=None, pad_inches=0.1): + transparent=False, bbox_inches=None, pad_inches=0.1) Save the current figure. diff --git a/lib/matplotlib/projections/__init__.py b/lib/matplotlib/projections/__init__.py index cbdd114d524f..d4652db31af0 100644 --- a/lib/matplotlib/projections/__init__.py +++ b/lib/matplotlib/projections/__init__.py @@ -46,6 +46,7 @@ def get_projection_names(self): def register_projection(cls): projection_registry.register(cls) + def get_projection_class(projection=None): """ Get a projection class from its name. @@ -61,6 +62,7 @@ def get_projection_class(projection=None): except KeyError: raise ValueError("Unknown projection '%s'" % projection) + def projection_factory(projection, figure, rect, **kwargs): """ Get a new projection instance. @@ -74,10 +76,56 @@ def projection_factory(projection, figure, rect, **kwargs): Any other kwargs are passed along to the specific projection constructor being used. + + .. deprecated:: + + This routine is deprecated in favour of getting the projection + class directly with :func:`get_projection_class` and initialising it + directly. Will be removed in version 1.3. + """ return get_projection_class(projection)(figure, rect, **kwargs) + +def process_projection_requirements(figure, *args, **kwargs): + """ + Handle the args/kwargs to for add_axes/add_subplot/gca, + returning:: + + (axes_proj_class, proj_class_kwargs, proj_stack_key) + + Which can be used for new axes initialization/identification. + + .. note:: **kwargs** is modified in place. + + """ + ispolar = kwargs.pop('polar', False) + projection = kwargs.pop('projection', None) + if ispolar: + if projection is not None and projection != 'polar': + raise ValueError( + "polar=True, yet projection=%r. " + "Only one of these arguments should be supplied." % + projection) + projection = 'polar' + + if isinstance(projection, basestring) or projection is None: + projection_class = get_projection_class(projection) + elif hasattr(projection, '_as_mpl_axes'): + projection_class, extra_kwargs = projection._as_mpl_axes() + kwargs.update(**extra_kwargs) + else: + raise TypeError('projection must be a string, None or implement a ' + '_as_mpl_axes method. Got %r' % projection) + + # Make the key without projection kwargs, this is used as a unique + # lookup for axes instances + key = figure._make_key(*args, **kwargs) + + return projection_class, kwargs, key + + def get_projection_names(): """ Get a list of acceptable projection names. diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index 429d93b87da8..a7be9c56fbd4 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -122,3 +122,6 @@ def test_tight_layout6(): h_pad=0.5) +if __name__=='__main__': + import nose + nose.runmodule(argv=['-s','--with-doctest'], exit=False) From 1b44a788aa6bad38bd145e2768df49efb68d0e11 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Wed, 6 Jun 2012 11:27:56 +0100 Subject: [PATCH 2/6] Added _as_mpl_axes test. --- lib/matplotlib/projections/polar.py | 7 ++-- lib/matplotlib/tests/test_axes.py | 56 ++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 332338ae50da..d1da569a7d7a 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -231,6 +231,9 @@ def __init__(self, *args, **kwargs): """ self.resolution = kwargs.pop('resolution', None) + self._default_theta_offset = kwargs.pop('theta_offset', 0) + self._default_theta_direction = kwargs.pop('theta_direction', 1) + if self.resolution not in (None, 1): warnings.warn( """The resolution kwarg to Polar plots is now ignored. @@ -259,8 +262,8 @@ def cla(self): # Why do we need to turn on yaxis tick labels, but # xaxis tick labels are already on? - self.set_theta_offset(0) - self.set_theta_direction(1) + self.set_theta_offset(self._default_theta_offset) + self.set_theta_direction(self._default_theta_direction) def _init_axis(self): "move this out of __init__ because non-separable axes don't use it" diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index b8c31f1bac68..232c5b474ca7 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -272,16 +272,14 @@ def test_polar_wrap(): fig = plt.figure() - #NOTE: resolution=1 really should be the default - plt.subplot( 111, polar=True, resolution=1 ) + plt.subplot(111, polar=True) plt.polar( [179*D2R, -179*D2R], [0.2, 0.1], "b.-" ) plt.polar( [179*D2R, 181*D2R], [0.2, 0.1], "g.-" ) plt.rgrids( [0.05, 0.1, 0.15, 0.2, 0.25, 0.3] ) fig = plt.figure() - #NOTE: resolution=1 really should be the default - plt.subplot( 111, polar=True, resolution=1 ) + plt.subplot( 111, polar=True) plt.polar( [2*D2R, -2*D2R], [0.2, 0.1], "b.-" ) plt.polar( [2*D2R, 358*D2R], [0.2, 0.1], "g.-" ) plt.polar( [358*D2R, 2*D2R], [0.2, 0.1], "r.-" ) @@ -713,6 +711,56 @@ def test_scatter_plot(): ax.scatter([3, 4, 2, 6], [2, 5, 2, 3], c=['r', 'y', 'b', 'lime'], s=[24, 15, 19, 29]) +def test_as_mpl_axes_api(): + # tests the _as_mpl_axes api + from matplotlib.projections.polar import PolarAxes + import matplotlib.axes as maxes + + class Polar(object): + def __init__(self): + self.theta_offset = 0 + + def _as_mpl_axes(self): + # implement the matplotlib axes interface + return PolarAxes, {'theta_offset': self.theta_offset} + prj = Polar() + prj2 = Polar() + prj2.theta_offset = np.pi + prj3 = Polar() + + # testing axes creation with plt.axes + ax = plt.axes([0, 0, 1, 1], projection=prj) + assert type(ax) == PolarAxes, \ + 'Expected a PolarAxes, got %s' % type(ax) + ax_via_gca = plt.gca(projection=prj) + # ideally, ax_via_gca is ax should be true. However, gca isn't + # plummed like that. (even with projection='polar'). + assert ax_via_gca is not ax + plt.close() + + # testing axes creation with gca + ax = plt.gca(projection=prj) + assert type(ax) == maxes._subplot_classes[PolarAxes], \ + 'Expected a PolarAxesSubplot, got %s' % type(ax) + ax_via_gca = plt.gca(projection=prj) + assert ax_via_gca is ax + # try getting the axes given a different polar projection + ax_via_gca = plt.gca(projection=prj2) + assert ax_via_gca is not ax + assert ax.get_theta_offset() == 0, ax.get_theta_offset() + assert ax_via_gca.get_theta_offset() == np.pi, ax_via_gca.get_theta_offset() + # try getting the axes given an == (not is) polar projection + ax_via_gca = plt.gca(projection=prj3) + assert ax_via_gca is ax + plt.close() + + # testing axes creation with subplot + ax = plt.subplot(121, projection=prj) + assert type(ax) == maxes._subplot_classes[PolarAxes], \ + 'Expected a PolarAxesSubplot, got %s' % type(ax) + plt.close() + + if __name__=='__main__': import nose nose.runmodule(argv=['-s','--with-doctest'], exit=False) From 8a3bdfc9f2593003921c7cfdcf2cee7c2cbb21de Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Wed, 6 Jun 2012 12:32:56 +0100 Subject: [PATCH 3/6] Fixed polar resolution handling. All tests passing (9 known fails). --- lib/matplotlib/projections/__init__.py | 7 +++++++ lib/matplotlib/projections/polar.py | 4 ++-- lib/matplotlib/pyplot.py | 2 +- lib/matplotlib/tests/test_axes.py | 5 ++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/projections/__init__.py b/lib/matplotlib/projections/__init__.py index d4652db31af0..14498ceaad6d 100644 --- a/lib/matplotlib/projections/__init__.py +++ b/lib/matplotlib/projections/__init__.py @@ -110,6 +110,13 @@ def process_projection_requirements(figure, *args, **kwargs): projection) projection = 'polar' + # ensure that the resolution keyword is always put into the key + # for polar plots [so that the result of + # plt.subplot(111, projection='polar') can be found with + # plt.gca(projection='polar', resolution=1)] + if projection == 'polar': + kwargs.setdefault('resolution', 1) + if isinstance(projection, basestring) or projection is None: projection_class = get_projection_class(projection) elif hasattr(projection, '_as_mpl_axes'): diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index d1da569a7d7a..56b25dfb1aac 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -229,8 +229,8 @@ def __init__(self, *args, **kwargs): each pair of data points. Set to 1 to disable interpolation. """ - - self.resolution = kwargs.pop('resolution', None) + self._rpad = 0.05 + self.resolution = kwargs.pop('resolution', 1) self._default_theta_offset = kwargs.pop('theta_offset', 0) self._default_theta_direction = kwargs.pop('theta_direction', 1) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index f6b814ba88fd..08f9e1b4096b 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2063,7 +2063,7 @@ def polar(*args, **kwargs): interpolate between each pair of points. The default is 1, which disables interpolation. """ - resolution = kwargs.pop('resolution', None) + resolution = kwargs.pop('resolution', 1) ax = gca(polar=True, resolution=resolution) ret = ax.plot(*args, **kwargs) draw_if_interactive() diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 232c5b474ca7..d0568ab35cdc 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -264,6 +264,7 @@ def test_const_xy(): plt.subplot( 313 ) plt.plot( np.ones( (10,) ), np.ones( (10,) ), 'o' ) + @image_comparison(baseline_images=['polar_wrap_180', 'polar_wrap_360', ]) @@ -273,10 +274,11 @@ def test_polar_wrap(): fig = plt.figure() plt.subplot(111, polar=True) + plt.polar( [179*D2R, -179*D2R], [0.2, 0.1], "b.-" ) plt.polar( [179*D2R, 181*D2R], [0.2, 0.1], "g.-" ) plt.rgrids( [0.05, 0.1, 0.15, 0.2, 0.25, 0.3] ) - + assert len(fig.axes) == 1, 'More than one polar axes created.' fig = plt.figure() plt.subplot( 111, polar=True) @@ -285,6 +287,7 @@ def test_polar_wrap(): plt.polar( [358*D2R, 2*D2R], [0.2, 0.1], "r.-" ) plt.rgrids( [0.05, 0.1, 0.15, 0.2, 0.25, 0.3] ) + @image_comparison(baseline_images=['polar_units', 'polar_units_2'], freetype_version=('2.4.5', '2.4.9')) def test_polar_units(): From 3a4241c2f499b3a0a9967bda07801c59b7b22abc Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Wed, 6 Jun 2012 12:39:47 +0100 Subject: [PATCH 4/6] Avoid changing things unneccesarily. --- lib/matplotlib/axes.py | 8 ++------ lib/matplotlib/projections/__init__.py | 4 +--- lib/matplotlib/projections/polar.py | 1 - lib/matplotlib/tests/test_axes.py | 1 - lib/matplotlib/tests/test_tightlayout.py | 4 ---- 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index a6aa19f5332a..42208cd1e71b 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -3514,9 +3514,7 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): * draw a gray rectangle from *y* = 0.25-0.75 that spans the horizontal extent of the axes:: - # draw a gray rectangle from *y* = 0.25-0.75 that spans the - # horizontal extent of the axes - axhspan(0.25, 0.75, facecolor='0.5', alpha=0.5) + >>> axhspan(0.25, 0.75, facecolor='0.5', alpha=0.5) Valid kwargs are :class:`~matplotlib.patches.Polygon` properties: @@ -3572,9 +3570,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): * draw a vertical green translucent rectangle from x=1.25 to 1.55 that spans the yrange of the axes:: - # draw a vertical green translucent rectangle from x=1.25 to 1.55 - # that spans the yrange of the axes - axvspan(1.25, 1.55, facecolor='g', alpha=0.5) + >>> axvspan(1.25, 1.55, facecolor='g', alpha=0.5) Valid kwargs are :class:`~matplotlib.patches.Polygon` properties: diff --git a/lib/matplotlib/projections/__init__.py b/lib/matplotlib/projections/__init__.py index 14498ceaad6d..2a7aafa65860 100644 --- a/lib/matplotlib/projections/__init__.py +++ b/lib/matplotlib/projections/__init__.py @@ -111,9 +111,7 @@ def process_projection_requirements(figure, *args, **kwargs): projection = 'polar' # ensure that the resolution keyword is always put into the key - # for polar plots [so that the result of - # plt.subplot(111, projection='polar') can be found with - # plt.gca(projection='polar', resolution=1)] + # for polar plots if projection == 'polar': kwargs.setdefault('resolution', 1) diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 56b25dfb1aac..37c9494a1329 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -229,7 +229,6 @@ def __init__(self, *args, **kwargs): each pair of data points. Set to 1 to disable interpolation. """ - self._rpad = 0.05 self.resolution = kwargs.pop('resolution', 1) self._default_theta_offset = kwargs.pop('theta_offset', 0) self._default_theta_direction = kwargs.pop('theta_direction', 1) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index d0568ab35cdc..d32cf66b2aea 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -713,7 +713,6 @@ def test_scatter_plot(): ax = plt.axes() ax.scatter([3, 4, 2, 6], [2, 5, 2, 3], c=['r', 'y', 'b', 'lime'], s=[24, 15, 19, 29]) - def test_as_mpl_axes_api(): # tests the _as_mpl_axes api from matplotlib.projections.polar import PolarAxes diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index a7be9c56fbd4..e0bdea662454 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -121,7 +121,3 @@ def test_tight_layout6(): None, 1 - (gs2.top-top)], h_pad=0.5) - -if __name__=='__main__': - import nose - nose.runmodule(argv=['-s','--with-doctest'], exit=False) From ea9a5b356a230931bd41dee5ae1598b4829ab14e Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Mon, 11 Jun 2012 15:33:27 +0100 Subject: [PATCH 5/6] Improved api_changes documentation. --- doc/api/api_changes.rst | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index d944ad4af621..a077f851101f 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -22,16 +22,35 @@ Changes in 1.2.x now set the radius of the pie; setting the *radius* to 'None' (the default value), will result in a pie with a radius of 1 as before. -* Use of :func:`matplotlib.projections.projection_factory` is now deprecated +* Use of :func:`~matplotlib.projections.projection_factory` is now deprecated in favour of axes class identification using - :func:`matplotlib.projections.process_projection_requirements` followed by - direct axes class invocation (at the time of writing, this is done by - :meth:`matplotlib.figure.Figure.add_axes`, - :meth:`matplotlib.figure.Figure.add_subplot` and - :meth:`matplotlib.figure.Figure.gca`. + :func:`~matplotlib.projections.process_projection_requirements` followed by + direct axes class invocation (at the time of writing, functions which do this + are: :meth:`~matplotlib.figure.Figure.add_axes`, + :meth:`~matplotlib.figure.Figure.add_subplot` and + :meth:`~matplotlib.figure.Figure.gca`). Therefore:: + + + key = figure._make_key(*args, **kwargs) + ispolar = kwargs.pop('polar', False) + projection = kwargs.pop('projection', None) + if ispolar: + if projection is not None and projection != 'polar': + raise ValuError('polar and projection args are inconsistent') + projection = 'polar' + ax = projection_factory(projection, self, rect, **kwargs) + key = self._make_key(*args, **kwargs) + + # is now + + projection_class, kwargs, key = \ + process_projection_requirements(self, *args, **kwargs) + ax = projection_class(self, rect, **kwargs) + This change means that third party objects can expose themselves as - matplotlib axes by providing a ``_as_mpl_axes`` method (see - :ref:`adding-new-scales` for more detail). + matplotlib axes by providing a ``_as_mpl_axes`` method. See + :ref:`adding-new-scales` for more detail. + Changes in 1.1.x ================ From cdce7afbce7dcde15997ecaa51b12bd2d6b8b533 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Mon, 11 Jun 2012 16:51:36 +0100 Subject: [PATCH 6/6] Fixed attribute name doc. --- doc/devel/add_new_projection.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/devel/add_new_projection.rst b/doc/devel/add_new_projection.rst index a823bd9baf72..cf3109380204 100644 --- a/doc/devel/add_new_projection.rst +++ b/doc/devel/add_new_projection.rst @@ -104,11 +104,11 @@ elements: Once the projection axes is defined, it can be used in one of two ways: - - By defining the class attribute ``NAME``, the projection axes can be + - By defining the class attribute ``name``, the projection axes can be registered with :func:`matplotlib.projections.register_projection` and subsequently simply invoked by name:: - plt.axes(projection=NAME) + plt.axes(projection='my_proj_name') - For more complex, parameterisable projections, a generic "projection" object may be defined which includes the method ``_as_mpl_axes``.