From fa87fa74f4ac01c1979b58a8927a0fa01ac289d3 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 23 Nov 2014 00:31:32 -0500 Subject: [PATCH 1/4] TST : tweak test_add_collection This is needed because this test tries to add artists from one axes to another which is not really supported. Not 100% that this is fully equivalent to the current test. There is a call to `add_collection` in scatter, but the test might be defeated by some of the other logic in scatter. --- lib/matplotlib/tests/test_collections.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index e4e709ffc0f0..043ec4c25ad7 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -407,14 +407,12 @@ def test_null_collection_datalim(): def test_add_collection(): # Test if data limits are unchanged by adding an empty collection. # Github issue #1490, pull #1497. - ax = plt.axes() plt.figure() - ax2 = plt.axes() - coll = ax2.scatter([0, 1], [0, 1]) + ax = plt.axes() + coll = ax.scatter([0, 1], [0, 1]) ax.add_collection(coll) bounds = ax.dataLim.bounds - coll = ax2.scatter([], []) - ax.add_collection(coll) + coll = ax.scatter([], []) assert_equal(ax.dataLim.bounds, bounds) From 394447fe991cca79768b6cf520f99eb9d745deea Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 23 Nov 2014 22:46:11 -0500 Subject: [PATCH 2/4] MNT : fix `__init__` order in AnchoredSizeLocator Trying to set the axes property before calling up the mro to the base class (eventually Artist) `__init__` which means that the `_axes` attribute is not yet attached to the instance object. - removed setting of `self.axes`, this is taken care of in the baseclass - moved rest of sub-class specific attributes to after call up mro stack --- lib/mpl_toolkits/axes_grid1/inset_locator.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index feb4a0316e8c..8daa198f7f0c 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -58,20 +58,21 @@ def __call__(self, ax, renderer): return bb + from . import axes_size as Size class AnchoredSizeLocator(AnchoredLocatorBase): def __init__(self, bbox_to_anchor, x_size, y_size, loc, borderpad=0.5, bbox_transform=None): - self.axes = None - self.x_size = Size.from_any(x_size) - self.y_size = Size.from_any(y_size) super(AnchoredSizeLocator, self).__init__(bbox_to_anchor, None, loc, borderpad=borderpad, bbox_transform=bbox_transform) + self.x_size = Size.from_any(x_size) + self.y_size = Size.from_any(y_size) + def get_extent(self, renderer): x, y, w, h = self.get_bbox_to_anchor().bounds From 69decec0297f5693303297800e5e05252b4663f9 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Nov 2014 21:57:13 -0500 Subject: [PATCH 3/4] MNT : tweak how internals of Axes.plot work The actual processing and artist creation in Axes.plot happens through the `__call__` method on a _process_plot_var_args instance hanging off the Axes object. The _process_plot_var_args knows what axes it is hanging off of and passed that into the Line2D artists as a kwarg at creation time, which sets the axes of the Line2D. The axes of the Line2D is set again during the `Axes.add_line` method. For the most part this is fine, if un-needed, because the axes is the same objects both time it is set. However when using the mpl_toolkit, Axes objects get wrapped in not-strictly-subclassed objects and the Axes object that the _process_plot_var_args object knows about is not the Axes object that add_line is called on which now results in an exception as it is moving artists between axes. --- lib/matplotlib/axes/_base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index eff223f92bb4..5f4035a8faa0 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -237,7 +237,6 @@ def _makeline(self, x, y, kw, kwargs): # (can't use setdefault because it always evaluates # its second argument) seg = mlines.Line2D(x, y, - axes=self.axes, **kw ) self.set_lineprops(seg, **kwargs) From c137a7186852d01980af8c53c3025788f677fab6 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 23 Nov 2014 00:37:28 -0500 Subject: [PATCH 4/4] API : property-ify Artist.axes - deprecate Artist.{get,set}_axes - add axes property - Raise exception when trying to move artists between axes. --- .../api_changes/2014-12-12_axes_property.rst | 16 +++++++ lib/matplotlib/artist.py | 44 ++++++++++++++++--- lib/matplotlib/axes/_base.py | 7 +-- lib/matplotlib/figure.py | 6 ++- lib/matplotlib/legend.py | 6 ++- lib/matplotlib/lines.py | 8 ++-- 6 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 doc/api/api_changes/2014-12-12_axes_property.rst diff --git a/doc/api/api_changes/2014-12-12_axes_property.rst b/doc/api/api_changes/2014-12-12_axes_property.rst new file mode 100644 index 000000000000..3e98a5cbd4a5 --- /dev/null +++ b/doc/api/api_changes/2014-12-12_axes_property.rst @@ -0,0 +1,16 @@ +Prevent moving artists between Axes, Property-ify Artist.axes, deprecate Artist.{get,set}_axes +`````````````````````````````````````````````````````````````````````````````````````````````` + +The reason this was done was to prevent adding an Artist that is +already associated with an Axes to be moved/added to a different Axes. +This was never supported as it causes havoc with the transform stack. +The apparent support for this (as it did not raise an exception) was +the source of multiple bug reports and questions on SO. + +For almost all use-cases, the assignment of the axes to an artist should be +taken care of by the axes as part of the ``Axes.add_*`` method, hence the +deprecation {get,set}_axes. + +Removing the ``set_axes`` method will also remove the 'axes' line from +the ACCEPTS kwarg tables (assuming that the removal date gets here +before that gets overhauled). diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index af9155c40dd6..7fb492367f1d 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -8,6 +8,7 @@ import inspect import matplotlib import matplotlib.cbook as cbook +from matplotlib.cbook import mplDeprecation from matplotlib import docstring, rcParams from .transforms import (Bbox, IdentityTransform, TransformedBbox, TransformedPath, Transform) @@ -77,6 +78,7 @@ class Artist(object): zorder = 0 def __init__(self): + self._axes = None self.figure = None self._transform = None @@ -175,17 +177,43 @@ def set_axes(self, axes): Set the :class:`~matplotlib.axes.Axes` instance in which the artist resides, if any. + This has been deprecated in mpl 1.5, please use the + axes property. Will be removed in 1.7 or 2.0. + ACCEPTS: an :class:`~matplotlib.axes.Axes` instance """ + warnings.warn(_get_axes_msg, mplDeprecation, stacklevel=1) self.axes = axes def get_axes(self): """ Return the :class:`~matplotlib.axes.Axes` instance the artist - resides in, or *None* + resides in, or *None*. + + This has been deprecated in mpl 1.5, please use the + axes property. Will be removed in 1.7 or 2.0. """ + warnings.warn(_get_axes_msg, mplDeprecation, stacklevel=1) return self.axes + @property + def axes(self): + """ + The :class:`~matplotlib.axes.Axes` instance the artist + resides in, or *None*. + """ + return self._axes + + @axes.setter + def axes(self, new_axes): + if self._axes is not None and new_axes != self._axes: + raise ValueError("Can not reset the axes. You are " + "probably trying to re-use an artist " + "in more than one Axes which is not " + "supported") + self._axes = new_axes + return new_axes + def get_window_extent(self, renderer): """ Get the axes bounding box in display space. @@ -751,10 +779,13 @@ def update(self, props): changed = False for k, v in six.iteritems(props): - func = getattr(self, 'set_' + k, None) - if func is None or not six.callable(func): - raise AttributeError('Unknown property %s' % k) - func(v) + if k in ['axes']: + setattr(self, k, v) + else: + func = getattr(self, 'set_' + k, None) + if func is None or not six.callable(func): + raise AttributeError('Unknown property %s' % k) + func(v) changed = True self.eventson = store if changed: @@ -1328,3 +1359,6 @@ def kwdoc(a): return '\n'.join(ArtistInspector(a).pprint_setters(leadingspace=2)) docstring.interpd.update(Artist=kwdoc(Artist)) + +_get_axes_msg = """This has been deprecated in mpl 1.5, please use the +axes property. A removal date has not been set.""" diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 5f4035a8faa0..03a5e4ed07e4 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -392,7 +392,8 @@ def __init__(self, fig, rect, else: self._position = mtransforms.Bbox.from_bounds(*rect) self._originalPosition = self._position.frozen() - self.set_axes(self) + # self.set_axes(self) + self.axes = self self.set_aspect('auto') self._adjustable = 'box' self.set_anchor('C') @@ -774,7 +775,7 @@ def _set_artist_props(self, a): if not a.is_transform_set(): a.set_transform(self.transData) - a.set_axes(self) + a.axes = self def _gen_axes_patch(self): """ @@ -1431,7 +1432,7 @@ def add_artist(self, a): Returns the artist. """ - a.set_axes(self) + a.axes = self self.artists.append(a) self._set_artist_props(a) a.set_clip_path(self.patch) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 34bfc30c503e..85ad1e6ba48a 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -298,7 +298,11 @@ def __init__(self, Defaults to rc ``figure.autolayout``. """ Artist.__init__(self) - + # remove the non-figure artist _axes property + # as it makes no sense for a figure to be _in_ an axes + # this is used by the property methods in the artist base class + # which are over-ridden in this class + del self._axes self.callbacks = cbook.CallbackRegistry() if figsize is None: diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index e5c0fc47a05a..e0536f5bfd44 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -304,7 +304,7 @@ def __init__(self, parent, handles, labels, if isinstance(parent, Axes): self.isaxes = True - self.set_axes(parent) + self.axes = parent self.set_figure(parent.figure) elif isinstance(parent, Figure): self.isaxes = False @@ -391,7 +391,9 @@ def _set_artist_props(self, a): """ a.set_figure(self.figure) if self.isaxes: - a.set_axes(self.axes) + # a.set_axes(self.axes) + a.axes = self.axes + a.set_transform(self.get_transform()) def _set_loc(self, loc): diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 87bb99896f7d..dd9be576f1b9 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -536,15 +536,17 @@ def get_window_extent(self, renderer): bbox = bbox.padded(ms) return bbox - def set_axes(self, ax): - Artist.set_axes(self, ax) + @Artist.axes.setter + def axes(self, ax): + # call the set method from the base-class property + Artist.axes.fset(self, ax) + # connect unit-related callbacks if ax.xaxis is not None: self._xcid = ax.xaxis.callbacks.connect('units', self.recache_always) if ax.yaxis is not None: self._ycid = ax.yaxis.callbacks.connect('units', self.recache_always) - set_axes.__doc__ = Artist.set_axes.__doc__ def set_data(self, *args): """ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy