@@ -56,22 +56,40 @@ class _AxesStack(cbook.Stack):
56
56
Specialization of `.Stack`, to handle all tracking of `~.axes.Axes` in a
57
57
`.Figure`.
58
58
59
- This stack stores ``key, axes`` pairs, where **key** is a hash of the args
60
- and kwargs used in generating the Axes.
59
+ This stack stores ``key, (ind, axes)`` pairs, where:
60
+
61
+ * **key** is a hash of the args used in generating the Axes.
62
+ * **ind** is a serial index tracking the order in which Axes were added.
61
63
62
64
AxesStack is a callable; calling it returns the current Axes.
63
65
The `current_key_axes` method returns the current key and associated Axes.
64
66
"""
65
67
68
+ def __init__ (self ):
69
+ super ().__init__ ()
70
+ self ._ind = 0
71
+
66
72
def as_list (self ):
67
73
"""
68
74
Return a list of the Axes instances that have been added to the figure.
69
75
"""
70
- return [a for k , a in self ._elements ]
76
+ ia_list = [a for k , a in self ._elements ]
77
+ ia_list .sort ()
78
+ return [a for i , a in ia_list ]
79
+
80
+ def get (self , key ):
81
+ """
82
+ Return the Axes instance that was added with *key*.
83
+ If it is not present, return *None*.
84
+ """
85
+ item = dict (self ._elements ).get (key )
86
+ if item is None :
87
+ return None
88
+ return item [1 ]
71
89
72
90
def _entry_from_axes (self , e ):
73
- k = {a : k for k , a in self ._elements }[e ]
74
- return (k , e )
91
+ ind , k = {a : ( ind , k ) for k , ( ind , a ) in self ._elements }[e ]
92
+ return (k , ( ind , e ) )
75
93
76
94
def remove (self , a ):
77
95
"""Remove the Axes from the stack."""
@@ -94,18 +112,33 @@ def add(self, key, a):
94
112
# All the error checking may be unnecessary; but this method
95
113
# is called so seldom that the overhead is negligible.
96
114
cbook ._check_isinstance (Axes , a = a )
97
- return super ().push ((key , a ))
115
+
116
+ a_existing = self .get (key )
117
+ if a_existing is not None :
118
+ super ().remove ((key , a_existing ))
119
+ cbook ._warn_external (
120
+ "key {!r} already existed; Axes is being replaced" .format (key ))
121
+ # I don't think the above should ever happen.
122
+
123
+ if a in self :
124
+ return None
125
+ self ._ind += 1
126
+ return super ().push ((key , (self ._ind , a )))
98
127
99
128
def current_key_axes (self ):
100
129
"""
101
130
Return a tuple of ``(key, axes)`` for the active Axes.
102
131
103
132
If no Axes exists on the stack, then returns ``(None, None)``.
104
133
"""
105
- return super ().__call__ () or (None , None )
134
+ if not len (self ._elements ):
135
+ return self ._default , self ._default
136
+ else :
137
+ key , (index , axes ) = self ._elements [self ._pos ]
138
+ return key , axes
106
139
107
140
def __call__ (self ):
108
- ka = self .current_key_axes ()[1 ]
141
+ return self .current_key_axes ()[1 ]
109
142
110
143
def __contains__ (self , a ):
111
144
return a in self .as_list ()
@@ -643,11 +676,11 @@ def add_axes(self, *args, **kwargs):
643
676
args = (kwargs .pop ('rect' ), )
644
677
645
678
if isinstance (args [0 ], Axes ):
646
- key = self ._make_key (* args , ** kwargs )
647
679
a = args [0 ]
648
680
if a .get_figure () is not self :
649
681
raise ValueError (
650
682
"The Axes must have been created in the present figure" )
683
+ key = self ._make_key (* args )
651
684
else :
652
685
rect = args [0 ]
653
686
if not np .isfinite (rect ).all ():
@@ -656,6 +689,13 @@ def add_axes(self, *args, **kwargs):
656
689
projection_class , kwargs , key = \
657
690
self ._process_projection_requirements (* args , ** kwargs )
658
691
692
+ # check that an axes of this type doesn't already exist, if it
693
+ # does, set it as active and return it
694
+ ax = self ._axstack .get (key )
695
+ if isinstance (ax , projection_class ):
696
+ self .sca (ax )
697
+ return ax
698
+
659
699
# create the new axes using the axes class given
660
700
a = projection_class (self , rect , ** kwargs )
661
701
return self ._add_axes_internal (key , a )
@@ -787,7 +827,7 @@ def add_subplot(self, *args, **kwargs):
787
827
"the present figure" )
788
828
# make a key for the subplot (which includes the axes object id
789
829
# in the hash)
790
- key = self ._make_key (* args , ** kwargs )
830
+ key = self ._make_key (* args )
791
831
792
832
else :
793
833
if not args :
@@ -800,6 +840,19 @@ def add_subplot(self, *args, **kwargs):
800
840
args = tuple (map (int , str (args [0 ])))
801
841
projection_class , kwargs , key = \
802
842
self ._process_projection_requirements (* args , ** kwargs )
843
+ ax = self ._axstack .get (key ) # search axes with this key in stack
844
+ if ax is not None :
845
+ if isinstance (ax , projection_class ):
846
+ # the axes already existed, so set it as active & return
847
+ self .sca (ax )
848
+ return ax
849
+ else :
850
+ # Undocumented convenience behavior:
851
+ # subplot(111); subplot(111, projection='polar')
852
+ # will replace the first with the second.
853
+ # Without this, add_subplot would be simpler and
854
+ # more similar to add_axes.
855
+ self ._axstack .remove (ax )
803
856
ax = subplot_class_factory (projection_class )(self , * args , ** kwargs )
804
857
return self ._add_axes_internal (key , ax )
805
858
@@ -1531,43 +1584,19 @@ def gca(self, **kwargs):
1531
1584
cbook .warn_deprecated (
1532
1585
"3.4" ,
1533
1586
message = "Calling gca() with keyword arguments is deprecated. "
1534
- "In a future version, gca() will take no keyword arguments. "
1535
- "The gca() function should only be used to get the current "
1536
- "axes, or if no axes exist, create new axes with default "
1537
- "keyword arguments. To create a new axes with non-default "
1538
- "arguments, use plt.axes() or plt.subplot()." )
1587
+ "gca() no longer checks whether the keyword arguments match "
1588
+ "those with which the current axes were created. In a future "
1589
+ "version, gca() will take no keyword arguments. The gca() "
1590
+ "function should only be used to get the current axes, or if "
1591
+ "no axes exist, create new axes with default keyword "
1592
+ "arguments. To create a new axes with non-default arguments, "
1593
+ "use plt.axes() or plt.subplot()." )
1539
1594
1540
1595
ckey , cax = self ._axstack .current_key_axes ()
1541
1596
# if there exists an axes on the stack see if it matches
1542
1597
# the desired axes configuration
1543
1598
if cax is not None :
1544
-
1545
- # if no kwargs are given just return the current axes
1546
- # this is a convenience for gca() on axes such as polar etc.
1547
- if not kwargs :
1548
- return cax
1549
-
1550
- # if the user has specified particular projection detail
1551
- # then build up a key which can represent this
1552
- else :
1553
- projection_class , _ , key = \
1554
- self ._process_projection_requirements (** kwargs )
1555
-
1556
- # let the returned axes have any gridspec by removing it from
1557
- # the key
1558
- ckey = ckey [1 :]
1559
- key = key [1 :]
1560
-
1561
- # if the cax matches this key then return the axes, otherwise
1562
- # continue and a new axes will be created
1563
- if key == ckey and isinstance (cax , projection_class ):
1564
- return cax
1565
- else :
1566
- raise ValueError (
1567
- "The arguments passed to gca() did not match the "
1568
- "arguments with which the current axes were "
1569
- "originally created. To create new axes, use "
1570
- "axes() or subplot()." )
1599
+ return cax
1571
1600
1572
1601
# no axes found, so create one which spans the figure
1573
1602
return self .add_subplot (1 , 1 , 1 , ** kwargs )
@@ -1643,28 +1672,12 @@ def _process_projection_requirements(
1643
1672
1644
1673
# Make the key without projection kwargs, this is used as a unique
1645
1674
# lookup for axes instances
1646
- key = self ._make_key (* args , ** kwargs )
1675
+ key = self ._make_key (* args )
1647
1676
1648
1677
return projection_class , kwargs , key
1649
1678
1650
- def _make_key (self , * args , ** kwargs ):
1651
- """Make a hashable key out of args and kwargs."""
1652
-
1653
- def fixitems (items ):
1654
- # items may have arrays and lists in them, so convert them
1655
- # to tuples for the key
1656
- ret = []
1657
- for k , v in items :
1658
- # some objects can define __getitem__ without being
1659
- # iterable and in those cases the conversion to tuples
1660
- # will fail. So instead of using the np.iterable(v) function
1661
- # we simply try and convert to a tuple, and proceed if not.
1662
- try :
1663
- v = tuple (v )
1664
- except Exception :
1665
- pass
1666
- ret .append ((k , v ))
1667
- return tuple (ret )
1679
+ def _make_key (self , * args ):
1680
+ """Make a hashable key out of args."""
1668
1681
1669
1682
def fixlist (args ):
1670
1683
ret = []
@@ -1674,7 +1687,7 @@ def fixlist(args):
1674
1687
ret .append (a )
1675
1688
return tuple (ret )
1676
1689
1677
- key = fixlist (args ), fixitems ( kwargs . items ())
1690
+ key = fixlist (args )
1678
1691
return key
1679
1692
1680
1693
def get_default_bbox_extra_artists (self ):
0 commit comments