Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit acd4bb2

Browse files
committed
Switch to a private, simpler AxesStack.
The current implementation of AxesStack subclasses cbook.Stack, which requires hashable keys, which leads to additional complexity on the caller's side (`_make_key`). Instead, switch to using two lists (keys and axes) and relying on `list.index`, which makes the implementation much simpler. Also make the new class private and deprecate the previous one.
1 parent 00ae164 commit acd4bb2

File tree

2 files changed

+69
-45
lines changed

2 files changed

+69
-45
lines changed

lib/matplotlib/figure.py

Lines changed: 68 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
import six
1818

19-
import warnings
2019
from operator import itemgetter
20+
import warnings
21+
from weakref import WeakKeyDictionary
2122

2223
import numpy as np
2324

@@ -73,6 +74,7 @@ class AxesStack(Stack):
7374
7475
"""
7576
def __init__(self):
77+
cbook.warn_deprecated("2.0")
7678
Stack.__init__(self)
7779
self._ind = 0
7880

@@ -157,6 +159,63 @@ def __contains__(self, a):
157159
return a in self.as_list()
158160

159161

162+
class _AxesStack(object):
163+
"""Lightweight stack that tracks Axes in a Figure.
164+
"""
165+
166+
# We do not subclass the Stack class from cbook to avoid hashability
167+
# issues.
168+
169+
def __init__(self):
170+
self._keys = []
171+
self._axes = []
172+
self._axes_order = WeakKeyDictionary()
173+
self._index = -1
174+
175+
axes = property(
176+
lambda self: sorted(self._axes, key=self._axes_order.__getitem__),
177+
doc="Copy of the list of axes, in the order of insertion.")
178+
179+
def get(self, key):
180+
"""Find the axes corresponding to a key; defaults to `None`.
181+
"""
182+
try:
183+
return self._axes[self._keys.index(key)]
184+
except ValueError:
185+
return None
186+
187+
def current_key_axes(self):
188+
"""Return the topmost key, axes pair, or `None, None` if empty.
189+
"""
190+
return (self._keys[-1], self._axes[-1]) if self._keys else (None, None)
191+
192+
def add(self, key, ax):
193+
"""Append a key, axes pair.
194+
"""
195+
self._keys.append(key)
196+
self._axes.append(ax)
197+
self._axes_order[ax] = self._index = self._index + 1
198+
199+
def bubble(self, ax):
200+
"""Move an axes and its corresponding key to the top.
201+
"""
202+
idx = self._axes.index(ax)
203+
self._keys.append(self._keys[idx])
204+
self._axes.append(self._axes[idx])
205+
del self._keys[idx], self._axes[idx]
206+
207+
def remove(self, ax):
208+
"""Remove an axes and its corresponding key.
209+
"""
210+
idx = self._axes.index(ax)
211+
del self._keys[idx], self._axes[idx]
212+
213+
def clear(self):
214+
"""Clear the stack.
215+
"""
216+
del self._keys[:], self._axes[:] # Py2 doesn't have list.clear.
217+
218+
160219
class SubplotParams(object):
161220
"""
162221
A class to hold the parameters for a subplot
@@ -350,7 +409,7 @@ def __init__(self,
350409
self.subplotpars = subplotpars
351410
self.set_tight_layout(tight_layout)
352411

353-
self._axstack = AxesStack() # track all figure axes and current axes
412+
self._axstack = _AxesStack() # track all figure axes and current axes
354413
self.clf()
355414
self._cachedRenderer = None
356415

@@ -399,7 +458,7 @@ def show(self, warn=True):
399458
"so cannot show the figure")
400459

401460
def _get_axes(self):
402-
return self._axstack.as_list()
461+
return self._axstack.axes
403462

404463
axes = property(fget=_get_axes, doc="Read-only: list of axes in Figure")
405464

@@ -812,36 +871,6 @@ def delaxes(self, a):
812871
func(self)
813872
self.stale = True
814873

815-
def _make_key(self, *args, **kwargs):
816-
'make a hashable key out of args and kwargs'
817-
818-
def fixitems(items):
819-
#items may have arrays and lists in them, so convert them
820-
# to tuples for the key
821-
ret = []
822-
for k, v in items:
823-
# some objects can define __getitem__ without being
824-
# iterable and in those cases the conversion to tuples
825-
# will fail. So instead of using the iterable(v) function
826-
# we simply try and convert to a tuple, and proceed if not.
827-
try:
828-
v = tuple(v)
829-
except Exception:
830-
pass
831-
ret.append((k, v))
832-
return tuple(ret)
833-
834-
def fixlist(args):
835-
ret = []
836-
for a in args:
837-
if iterable(a):
838-
a = tuple(a)
839-
ret.append(a)
840-
return tuple(ret)
841-
842-
key = fixlist(args), fixitems(six.iteritems(kwargs))
843-
return key
844-
845874
@docstring.dedent_interpd
846875
def add_axes(self, *args, **kwargs):
847876
"""
@@ -895,9 +924,9 @@ def add_axes(self, *args, **kwargs):
895924

896925
# shortcut the projection "key" modifications later on, if an axes
897926
# with the exact args/kwargs exists, return it immediately.
898-
key = self._make_key(*args, **kwargs)
927+
key = (args, kwargs)
899928
ax = self._axstack.get(key)
900-
if ax is not None:
929+
if ax:
901930
self.sca(ax)
902931
return ax
903932

@@ -914,7 +943,7 @@ def add_axes(self, *args, **kwargs):
914943
# check that an axes of this type doesn't already exist, if it
915944
# does, set it as active and return it
916945
ax = self._axstack.get(key)
917-
if ax is not None and isinstance(ax, projection_class):
946+
if isinstance(ax, projection_class):
918947
self.sca(ax)
919948
return ax
920949

@@ -988,15 +1017,14 @@ def add_subplot(self, *args, **kwargs):
9881017
raise ValueError(msg)
9891018
# make a key for the subplot (which includes the axes object id
9901019
# in the hash)
991-
key = self._make_key(*args, **kwargs)
1020+
key = (args, kwargs)
9921021
else:
9931022
projection_class, kwargs, key = process_projection_requirements(
9941023
self, *args, **kwargs)
9951024

9961025
# try to find the axes with this key in the stack
9971026
ax = self._axstack.get(key)
998-
999-
if ax is not None:
1027+
if ax:
10001028
if isinstance(ax, projection_class):
10011029
# the axes already existed, so set it as active & return
10021030
self.sca(ax)
@@ -1496,7 +1524,7 @@ def _gci(self):
14961524
do not use elsewhere.
14971525
"""
14981526
# Look first for an image in the current Axes:
1499-
cax = self._axstack.current_key_axes()[1]
1527+
ckey, cax = self._axstack.current_key_axes()
15001528
if cax is None:
15011529
return None
15021530
im = cax._gci()

lib/matplotlib/projections/__init__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,7 @@ def process_projection_requirements(figure, *args, **kwargs):
103103
raise TypeError('projection must be a string, None or implement a '
104104
'_as_mpl_axes method. Got %r' % projection)
105105

106-
# Make the key without projection kwargs, this is used as a unique
107-
# lookup for axes instances
108-
key = figure._make_key(*args, **kwargs)
109-
110-
return projection_class, kwargs, key
106+
return projection_class, kwargs, (args, kwargs)
111107

112108

113109
def get_projection_names():

0 commit comments

Comments
 (0)