From dd54033827fe3f64b97588cd117bc3d4f727cb46 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 14 Aug 2017 15:22:44 -0400 Subject: [PATCH 1/2] Revert "ENH: Switch to a private, simpler AxesStack." --- lib/matplotlib/figure.py | 108 ++++++++++--------------- lib/matplotlib/projections/__init__.py | 6 +- 2 files changed, 47 insertions(+), 67 deletions(-) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index db45e04410e6..0e22ac19034f 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -72,7 +72,6 @@ class AxesStack(Stack): """ def __init__(self): - cbook.warn_deprecated("2.1") Stack.__init__(self) self._ind = 0 @@ -159,62 +158,6 @@ def __contains__(self, a): return a in self.as_list() -class _AxesStack(object): - """Lightweight stack that tracks Axes in a Figure. - """ - - def __init__(self): - # We maintain a list of (creation_index, key, axes) tuples. - # We do not use an OrderedDict because 1. the keys may not be hashable - # and 2. we need to directly find a pair corresponding to an axes (i.e. - # we'd really need a two-way dict). - self._items = [] - self._created = 0 - - def as_list(self): - """Copy of the list of axes, in the order of insertion. - """ - return [ax for _, _, ax in sorted(self._items)] - - def get(self, key): - """Find the axes corresponding to a key; defaults to `None`. - """ - return next((ax for _, k, ax in self._items if k == key), None) - - def current_key_axes(self): - """Return the topmost `(key, axes)` pair, or `(None, None)` if empty. - """ - _, key, ax = (self._items or [(None, None, None)])[-1] - return key, ax - - def add(self, key, ax): - """Append a `(key, axes)` pair, unless the axes are already present. - """ - # Skipping existing Axes is needed to support calling `add_axes` with - # an already existing Axes. - if not any(a == ax for _, _, a in self._items): - self._items.append((self._created, key, ax)) - self._created += 1 - - def bubble(self, ax): - """Move an axes and its corresponding key to the top. - """ - idx, = (idx for idx, (_, _, a) in enumerate(self._items) if a == ax) - self._items.append(self._items[idx]) - del self._items[idx] - - def remove(self, ax): - """Remove an axes and its corresponding key. - """ - idx, = (idx for idx, (_, _, a) in enumerate(self._items) if a == ax) - del self._items[idx] - - def clear(self): - """Clear the stack. - """ - del self._items[:] - - class SubplotParams(object): """ A class to hold the parameters for a subplot @@ -415,7 +358,7 @@ def __init__(self, self.subplotpars = subplotpars self.set_tight_layout(tight_layout) - self._axstack = _AxesStack() # track all figure axes and current axes + self._axstack = AxesStack() # track all figure axes and current axes self.clf() self._cachedRenderer = None @@ -467,8 +410,10 @@ def show(self, warn=True): "matplotlib is currently using a non-GUI backend, " "so cannot show the figure") - axes = property(lambda self: self._axstack.as_list(), - doc="Read-only: list of axes in Figure") + def _get_axes(self): + return self._axstack.as_list() + + axes = property(fget=_get_axes, doc="Read-only: list of axes in Figure") def _get_dpi(self): return self._dpi @@ -890,6 +835,36 @@ def delaxes(self, a): func(self) self.stale = True + def _make_key(self, *args, **kwargs): + 'make a hashable key out of args and kwargs' + + def fixitems(items): + #items may have arrays and lists in them, so convert them + # to tuples for the key + ret = [] + for k, v in items: + # some objects can define __getitem__ without being + # iterable and in those cases the conversion to tuples + # will fail. So instead of using the iterable(v) function + # we simply try and convert to a tuple, and proceed if not. + try: + v = tuple(v) + except Exception: + pass + ret.append((k, v)) + return tuple(ret) + + def fixlist(args): + ret = [] + for a in args: + if iterable(a): + a = tuple(a) + ret.append(a) + return tuple(ret) + + key = fixlist(args), fixitems(six.iteritems(kwargs)) + return key + def add_axes(self, *args, **kwargs): """ Add an axes at position *rect* [*left*, *bottom*, *width*, @@ -954,9 +929,9 @@ def add_axes(self, *args, **kwargs): # shortcut the projection "key" modifications later on, if an axes # with the exact args/kwargs exists, return it immediately. - key = (args, kwargs) + key = self._make_key(*args, **kwargs) ax = self._axstack.get(key) - if ax: + if ax is not None: self.sca(ax) return ax @@ -976,7 +951,7 @@ def add_axes(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 isinstance(ax, projection_class): + if ax is not None and isinstance(ax, projection_class): self.sca(ax) return ax @@ -1062,14 +1037,15 @@ def add_subplot(self, *args, **kwargs): raise ValueError(msg) # make a key for the subplot (which includes the axes object id # in the hash) - key = (args, kwargs) + key = self._make_key(*args, **kwargs) else: 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: + + if ax is not None: if isinstance(ax, projection_class): # the axes already existed, so set it as active & return self.sca(ax) @@ -1638,7 +1614,7 @@ def _gci(self): do not use elsewhere. """ # Look first for an image in the current Axes: - ckey, cax = self._axstack.current_key_axes() + cax = self._axstack.current_key_axes()[1] if cax is None: return None im = cax._gci() diff --git a/lib/matplotlib/projections/__init__.py b/lib/matplotlib/projections/__init__.py index 5e5ffcaf2e66..1e423420b0b6 100644 --- a/lib/matplotlib/projections/__init__.py +++ b/lib/matplotlib/projections/__init__.py @@ -96,7 +96,11 @@ def process_projection_requirements(figure, *args, **kwargs): raise TypeError('projection must be a string, None or implement a ' '_as_mpl_axes method. Got %r' % projection) - return projection_class, kwargs, (args, kwargs) + # 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(): From d84c9ba27be71de638cb53daa5227ea1425aa48e Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 15 Aug 2017 04:29:00 -0700 Subject: [PATCH 2/2] Deprecate the Axes-reuse-if-same-args behavior. --- lib/matplotlib/figure.py | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 0e22ac19034f..4c6b042be5a4 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -91,6 +91,13 @@ def get(self, key): item = dict(self._elements).get(key) if item is None: return None + cbook.warn_deprecated( + "2.1", + "Adding an axes using the same arguments as a previous axes " + "currently reuses the earlier instance. In a future version, " + "a new instance will always be created and returned. Meanwhile, " + "this warning can be suppressed, and the future behavior ensured, " + "by passing a unique label to each axes instance.") return item[1] def _entry_from_axes(self, e): @@ -901,14 +908,14 @@ def add_axes(self, *args, **kwargs): fig.add_axes(rect, projection='polar') fig.add_axes(ax) - If the figure already has an axes with the same parameters, - then it will simply make that axes current and return it. If - you do not want this behavior, e.g., you want to force the - creation of a new Axes, you must use a unique set of args and - kwargs. The axes :attr:`~matplotlib.axes.Axes.label` - attribute has been exposed for this purpose. e.g., if you want - two axes that are otherwise identical to be added to the - figure, make sure you give them unique labels:: + If the figure already has an axes with the same parameters, then it + will simply make that axes current and return it. This behavior + has been deprecated as of Matplotlib 2.1. Meanwhile, if you do + not want this behavior (i.e., you want to force the creation of a + new Axes), you must use a unique set of args and kwargs. The axes + :attr:`~matplotlib.axes.Axes.label` attribute has been exposed for this + purpose: if you want two axes that are otherwise identical to be added + to the figure, make sure you give them unique labels:: fig.add_axes(rect, label='axes1') fig.add_axes(rect, label='axes2') @@ -996,14 +1003,14 @@ def add_subplot(self, *args, **kwargs): ----- If the figure already has a subplot with key (*args*, *kwargs*) then it will simply make that subplot current and - return it. + return it. This behavior is deprecated. Examples -------- fig.add_subplot(111) # equivalent but more general - fig.add_subplot(1,1,1) + fig.add_subplot(1, 1, 1) # add subplot with red background fig.add_subplot(212, facecolor='r') @@ -1022,18 +1029,17 @@ def add_subplot(self, *args, **kwargs): return if len(args) == 1 and isinstance(args[0], int): - args = tuple([int(c) for c in str(args[0])]) - if len(args) != 3: - raise ValueError("Integer subplot specification must " + - "be a three digit number. " + - "Not {n:d}".format(n=len(args))) + if not 100 <= args[0] <= 999: + raise ValueError("Integer subplot specification must be a " + "three-digit number, not {}".format(args[0])) + args = tuple(map(int, str(args[0]))) if isinstance(args[0], SubplotBase): a = args[0] if a.get_figure() is not self: - msg = ("The Subplot must have been created in the present" - " figure") + msg = ("The Subplot must have been created in the present " + "figure") raise ValueError(msg) # make a key for the subplot (which includes the axes object id # in the hash) 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