1616
1717import six
1818
19- import warnings
2019from operator import itemgetter
20+ import warnings
2121
2222import 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 (object ):
162+ """Lightweight stack that tracks Axes in a Figure.
163+ """
164+
165+ def __init__ (self ):
166+ # We maintain a list of (creation_index, key, axes) tuples.
167+ self ._items = []
168+ self ._created = 0
169+
170+ def as_list (self ):
171+ """Copy of the list of axes, in the order of insertion.
172+ """
173+ return [ax for _ , _ , ax in sorted (self ._items )]
174+
175+ def get (self , key ):
176+ """Find the axes corresponding to a key; defaults to `None`.
177+ """
178+ return next ((ax for _ , k , ax in self ._items if k == key ), None )
179+
180+ def current_key_axes (self ):
181+ """Return the topmost key, axes pair, or `None, None` if empty.
182+ """
183+ _ , key , ax = (self ._items or [(None , None , None )])[- 1 ]
184+ return key , ax
185+
186+ def add (self , key , ax ):
187+ """Append a key, axes pair, unless the axes are already present.
188+ """
189+ # Skipping existing Axes is needed to support calling `add_axes` with
190+ # an already existing Axes.
191+ if not any (a == ax for _ , _ , a in self ._items ):
192+ self ._items .append ((self ._created , key , ax ))
193+ self ._created += 1
194+
195+ def bubble (self , ax ):
196+ """Move an axes and its corresponding key to the top.
197+ """
198+ idx , = (idx for idx , (_ , _ , a ) in enumerate (self ._items ) if a == ax )
199+ self ._items .append (self ._items [idx ])
200+ del self ._items [idx ]
201+
202+ def remove (self , ax ):
203+ """Remove an axes and its corresponding key.
204+ """
205+ idx , = (idx for idx , (_ , _ , a ) in enumerate (self ._items ) if a == ax )
206+ del self ._items [idx ]
207+
208+ def clear (self ):
209+ """Clear the stack.
210+ """
211+ del self ._items [:]
212+
213+
160214class 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
@@ -398,10 +452,8 @@ def show(self, warn=True):
398452 "matplotlib is currently using a non-GUI backend, "
399453 "so cannot show the figure" )
400454
401- def _get_axes (self ):
402- return self ._axstack .as_list ()
403-
404- axes = property (fget = _get_axes , doc = "Read-only: list of axes in Figure" )
455+ axes = property (lambda self : self ._axstack .as_list (),
456+ doc = "Read-only: list of axes in Figure" )
405457
406458 def _get_dpi (self ):
407459 return self ._dpi
@@ -812,36 +864,6 @@ def delaxes(self, a):
812864 func (self )
813865 self .stale = True
814866
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-
845867 @docstring .dedent_interpd
846868 def add_axes (self , * args , ** kwargs ):
847869 """
@@ -895,9 +917,9 @@ def add_axes(self, *args, **kwargs):
895917
896918 # shortcut the projection "key" modifications later on, if an axes
897919 # with the exact args/kwargs exists, return it immediately.
898- key = self . _make_key ( * args , ** kwargs )
920+ key = ( args , kwargs )
899921 ax = self ._axstack .get (key )
900- if ax is not None :
922+ if ax :
901923 self .sca (ax )
902924 return ax
903925
@@ -914,7 +936,7 @@ def add_axes(self, *args, **kwargs):
914936 # check that an axes of this type doesn't already exist, if it
915937 # does, set it as active and return it
916938 ax = self ._axstack .get (key )
917- if ax is not None and isinstance (ax , projection_class ):
939+ if isinstance (ax , projection_class ):
918940 self .sca (ax )
919941 return ax
920942
@@ -988,15 +1010,14 @@ def add_subplot(self, *args, **kwargs):
9881010 raise ValueError (msg )
9891011 # make a key for the subplot (which includes the axes object id
9901012 # in the hash)
991- key = self . _make_key ( * args , ** kwargs )
1013+ key = ( args , kwargs )
9921014 else :
9931015 projection_class , kwargs , key = process_projection_requirements (
9941016 self , * args , ** kwargs )
9951017
9961018 # try to find the axes with this key in the stack
9971019 ax = self ._axstack .get (key )
998-
999- if ax is not None :
1020+ if ax :
10001021 if isinstance (ax , projection_class ):
10011022 # the axes already existed, so set it as active & return
10021023 self .sca (ax )
@@ -1496,7 +1517,7 @@ def _gci(self):
14961517 do not use elsewhere.
14971518 """
14981519 # Look first for an image in the current Axes:
1499- cax = self ._axstack .current_key_axes ()[ 1 ]
1520+ ckey , cax = self ._axstack .current_key_axes ()
15001521 if cax is None :
15011522 return None
15021523 im = cax ._gci ()
0 commit comments