3737
3838docstring .interpd .update (projection_names = get_projection_names ())
3939
40+ class AxesStack (Stack ):
41+ """
42+ Specialization of the Stack to handle all
43+ tracking of Axes in a Figure. This requires storing
44+ key, axes pairs. The key is based on the args and kwargs
45+ used in generating the Axes.
46+ """
47+ def as_list (self ):
48+ """
49+ Return a list of the Axes instances that have been added to the figure
50+ """
51+ return [a for k , a in self ._elements ]
52+
53+ def get (self , key ):
54+ """
55+ Return the Axes instance that was added with *key*.
56+ If it is not present, return None.
57+ """
58+ return dict (self ._elements ).get (key )
59+
60+ def _entry_from_axes (self , e ):
61+ k = dict ([(a , k ) for (k , a ) in self ._elements ])[e ]
62+ return k , e
63+
64+ def remove (self , a ):
65+ Stack .remove (self , self ._entry_from_axes (a ))
66+
67+ def bubble (self , a ):
68+ return Stack .bubble (self , self ._entry_from_axes (a ))
69+
70+ def add (self , key , a ):
71+ """
72+ Add Axes *a*, with key *key*, to the stack, and return the stack.
73+
74+ If *a* is already on the stack, don't add it again, but
75+ return *None*.
76+ """
77+ # All the error checking may be unnecessary; but this method
78+ # is called so seldom that the overhead is negligible.
79+ if not isinstance (a , Axes ):
80+ raise ValueError ("second argument, %s, is not an Axes" % a )
81+ try :
82+ hash (key )
83+ except TypeError :
84+ raise ValueError ("first argument, %s, is not a valid key" % key )
85+ if a in self :
86+ return None
87+ return Stack .push (self , (key , a ))
88+
89+ def __call__ (self ):
90+ if not len (self ._elements ):
91+ return self ._default
92+ else :
93+ return self ._elements [self ._pos ][1 ]
94+
95+ def __contains__ (self , a ):
96+ return a in self .as_list ()
97+
4098class SubplotParams :
4199 """
42100 A class to hold the parameters for a subplot
@@ -202,11 +260,15 @@ def __init__(self,
202260
203261 self .subplotpars = subplotpars
204262
205- self ._axstack = Stack () # maintain the current axes
206- self .axes = []
263+ self ._axstack = AxesStack () # track all figure axes and current axes
207264 self .clf ()
208265 self ._cachedRenderer = None
209266
267+ def _get_axes (self ):
268+ return self ._axstack .as_list ()
269+
270+ axes = property (fget = _get_axes , doc = "Read-only: list of axes in Figure" )
271+
210272 def _get_dpi (self ):
211273 return self ._dpi
212274 def _set_dpi (self , dpi ):
@@ -523,15 +585,9 @@ def set_frameon(self, b):
523585
524586 def delaxes (self , a ):
525587 'remove a from the figure and update the current axes'
526- self .axes .remove (a )
527588 self ._axstack .remove (a )
528- keys = []
529- for key , thisax in self ._seen .items ():
530- if a == thisax : del self ._seen [key ]
531589 for func in self ._axobservers : func (self )
532590
533-
534-
535591 def _make_key (self , * args , ** kwargs ):
536592 'make a hashable key out of args and kwargs'
537593
@@ -595,8 +651,8 @@ def add_axes(self, *args, **kwargs):
595651
596652 key = self ._make_key (* args , ** kwargs )
597653
598- if key in self ._seen :
599- ax = self . _seen [ key ]
654+ ax = self ._axstack . get ( key )
655+ if ax is not None :
600656 self .sca (ax )
601657 return ax
602658
@@ -618,10 +674,9 @@ def add_axes(self, *args, **kwargs):
618674
619675 a = projection_factory (projection , self , rect , ** kwargs )
620676
621- self .axes . append ( a )
622- self ._axstack .push ( a )
677+ if a not in self ._axstack :
678+ self ._axstack .add ( key , a )
623679 self .sca (a )
624- self ._seen [key ] = a
625680 return a
626681
627682 @docstring .dedent_interpd
@@ -675,19 +730,16 @@ def add_subplot(self, *args, **kwargs):
675730 projection_class = get_projection_class (projection )
676731
677732 key = self ._make_key (* args , ** kwargs )
678- if key in self ._seen :
679- ax = self . _seen [ key ]
733+ ax = self ._axstack . get ( key )
734+ if ax is not None :
680735 if isinstance (ax , projection_class ):
681736 self .sca (ax )
682737 return ax
683738 else :
684- self .axes .remove (ax )
685739 self ._axstack .remove (ax )
686740
687741 a = subplot_class_factory (projection_class )(self , * args , ** kwargs )
688- self ._seen [key ] = a
689- self .axes .append (a )
690- self ._axstack .push (a )
742+ self ._axstack .add (key , a )
691743 self .sca (a )
692744 return a
693745
@@ -703,13 +755,12 @@ def clf(self, keep_observers=False):
703755
704756 for ax in tuple (self .axes ): # Iterate over the copy.
705757 ax .cla ()
706- self .delaxes (ax ) # removes ax from self.axes
758+ self .delaxes (ax ) # removes ax from self._axstack
707759
708760 toolbar = getattr (self .canvas , 'toolbar' , None )
709761 if toolbar is not None :
710762 toolbar .update ()
711763 self ._axstack .clear ()
712- self ._seen = {}
713764 self .artists = []
714765 self .lines = []
715766 self .patches = []
@@ -975,7 +1026,7 @@ def _gci(self):
9751026 helper for :func:`~matplotlib.pyplot.gci`;
9761027 do not use elsewhere.
9771028 """
978- for ax in reversed (self ._axstack ):
1029+ for ax in reversed (self .axes ):
9791030 im = ax ._gci ()
9801031 if im is not None :
9811032 return im
0 commit comments