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

Skip to content

Commit d48d6cd

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 d48d6cd

File tree

2 files changed

+64
-45
lines changed

2 files changed

+64
-45
lines changed

lib/matplotlib/figure.py

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

1717
import six
1818

19-
import warnings
2019
from operator import itemgetter
20+
import warnings
2121

2222
import numpy as np
2323

@@ -73,6 +73,7 @@ class AxesStack(Stack):
7373
7474
"""
7575
def __init__(self):
76+
cbook.warn_deprecated("2.0")
7677
Stack.__init__(self)
7778
self._ind = 0
7879

@@ -157,6 +158,59 @@ def __contains__(self, a):
157158
return a in self.as_list()
158159

159160

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

353-
self._axstack = AxesStack() # track all figure axes and current axes
407+
self._axstack = _AxesStack() # track all figure axes and current axes
354408
self.clf()
355409
self._cachedRenderer = None
356410

@@ -399,7 +453,7 @@ def show(self, warn=True):
399453
"so cannot show the figure")
400454

401455
def _get_axes(self):
402-
return self._axstack.as_list()
456+
return self._axstack.axes
403457

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

@@ -812,36 +866,6 @@ def delaxes(self, a):
812866
func(self)
813867
self.stale = True
814868

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-
845869
@docstring.dedent_interpd
846870
def add_axes(self, *args, **kwargs):
847871
"""
@@ -895,9 +919,9 @@ def add_axes(self, *args, **kwargs):
895919

896920
# shortcut the projection "key" modifications later on, if an axes
897921
# with the exact args/kwargs exists, return it immediately.
898-
key = self._make_key(*args, **kwargs)
922+
key = (args, kwargs)
899923
ax = self._axstack.get(key)
900-
if ax is not None:
924+
if ax:
901925
self.sca(ax)
902926
return ax
903927

@@ -914,7 +938,7 @@ def add_axes(self, *args, **kwargs):
914938
# check that an axes of this type doesn't already exist, if it
915939
# does, set it as active and return it
916940
ax = self._axstack.get(key)
917-
if ax is not None and isinstance(ax, projection_class):
941+
if isinstance(ax, projection_class):
918942
self.sca(ax)
919943
return ax
920944

@@ -988,15 +1012,14 @@ def add_subplot(self, *args, **kwargs):
9881012
raise ValueError(msg)
9891013
# make a key for the subplot (which includes the axes object id
9901014
# in the hash)
991-
key = self._make_key(*args, **kwargs)
1015+
key = (args, kwargs)
9921016
else:
9931017
projection_class, kwargs, key = process_projection_requirements(
9941018
self, *args, **kwargs)
9951019

9961020
# try to find the axes with this key in the stack
9971021
ax = self._axstack.get(key)
998-
999-
if ax is not None:
1022+
if ax:
10001023
if isinstance(ax, projection_class):
10011024
# the axes already existed, so set it as active & return
10021025
self.sca(ax)
@@ -1496,7 +1519,7 @@ def _gci(self):
14961519
do not use elsewhere.
14971520
"""
14981521
# Look first for an image in the current Axes:
1499-
cax = self._axstack.current_key_axes()[1]
1522+
ckey, cax = self._axstack.current_key_axes()
15001523
if cax is None:
15011524
return None
15021525
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)