Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 41b93d8

Browse filesBrowse files
committed
WIP
1 parent 2d5e345 commit 41b93d8
Copy full SHA for 41b93d8

File tree

Expand file treeCollapse file tree

3 files changed

+77
-68
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+77
-68
lines changed

‎lib/matplotlib/figure.py

Copy file name to clipboardExpand all lines: lib/matplotlib/figure.py
+75-62Lines changed: 75 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,40 @@ class _AxesStack(cbook.Stack):
5656
Specialization of `.Stack`, to handle all tracking of `~.axes.Axes` in a
5757
`.Figure`.
5858
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.
6163
6264
AxesStack is a callable; calling it returns the current Axes.
6365
The `current_key_axes` method returns the current key and associated Axes.
6466
"""
6567

68+
def __init__(self):
69+
super().__init__()
70+
self._ind = 0
71+
6672
def as_list(self):
6773
"""
6874
Return a list of the Axes instances that have been added to the figure.
6975
"""
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]
7189

7290
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))
7593

7694
def remove(self, a):
7795
"""Remove the Axes from the stack."""
@@ -94,18 +112,33 @@ def add(self, key, a):
94112
# All the error checking may be unnecessary; but this method
95113
# is called so seldom that the overhead is negligible.
96114
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)))
98127

99128
def current_key_axes(self):
100129
"""
101130
Return a tuple of ``(key, axes)`` for the active Axes.
102131
103132
If no Axes exists on the stack, then returns ``(None, None)``.
104133
"""
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
106139

107140
def __call__(self):
108-
ka = self.current_key_axes()[1]
141+
return self.current_key_axes()[1]
109142

110143
def __contains__(self, a):
111144
return a in self.as_list()
@@ -643,11 +676,11 @@ def add_axes(self, *args, **kwargs):
643676
args = (kwargs.pop('rect'), )
644677

645678
if isinstance(args[0], Axes):
646-
key = self._make_key(*args, **kwargs)
647679
a = args[0]
648680
if a.get_figure() is not self:
649681
raise ValueError(
650682
"The Axes must have been created in the present figure")
683+
key = self._make_key(*args)
651684
else:
652685
rect = args[0]
653686
if not np.isfinite(rect).all():
@@ -656,6 +689,13 @@ def add_axes(self, *args, **kwargs):
656689
projection_class, kwargs, key = \
657690
self._process_projection_requirements(*args, **kwargs)
658691

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+
659699
# create the new axes using the axes class given
660700
a = projection_class(self, rect, **kwargs)
661701
return self._add_axes_internal(key, a)
@@ -787,7 +827,7 @@ def add_subplot(self, *args, **kwargs):
787827
"the present figure")
788828
# make a key for the subplot (which includes the axes object id
789829
# in the hash)
790-
key = self._make_key(*args, **kwargs)
830+
key = self._make_key(*args)
791831

792832
else:
793833
if not args:
@@ -800,6 +840,19 @@ def add_subplot(self, *args, **kwargs):
800840
args = tuple(map(int, str(args[0])))
801841
projection_class, kwargs, key = \
802842
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)
803856
ax = subplot_class_factory(projection_class)(self, *args, **kwargs)
804857
return self._add_axes_internal(key, ax)
805858

@@ -1531,43 +1584,19 @@ def gca(self, **kwargs):
15311584
cbook.warn_deprecated(
15321585
"3.4",
15331586
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().")
15391594

15401595
ckey, cax = self._axstack.current_key_axes()
15411596
# if there exists an axes on the stack see if it matches
15421597
# the desired axes configuration
15431598
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
15711600

15721601
# no axes found, so create one which spans the figure
15731602
return self.add_subplot(1, 1, 1, **kwargs)
@@ -1643,28 +1672,12 @@ def _process_projection_requirements(
16431672

16441673
# Make the key without projection kwargs, this is used as a unique
16451674
# lookup for axes instances
1646-
key = self._make_key(*args, **kwargs)
1675+
key = self._make_key(*args)
16471676

16481677
return projection_class, kwargs, key
16491678

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."""
16681681

16691682
def fixlist(args):
16701683
ret = []
@@ -1674,7 +1687,7 @@ def fixlist(args):
16741687
ret.append(a)
16751688
return tuple(ret)
16761689

1677-
key = fixlist(args), fixitems(kwargs.items())
1690+
key = fixlist(args)
16781691
return key
16791692

16801693
def get_default_bbox_extra_artists(self):

‎lib/matplotlib/tests/test_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_axes.py
+1-3Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2408,9 +2408,7 @@ def _as_mpl_axes(self):
24082408
# try getting the axes given a different polar projection
24092409
with pytest.warns(
24102410
MatplotlibDeprecationWarning,
2411-
match=r'Calling gca\(\) with keyword arguments is deprecated'), \
2412-
pytest.raises(
2413-
ValueError, match=r'arguments passed to gca\(\) did not match'):
2411+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
24142412
ax_via_gca = plt.gca(projection=prj2)
24152413
# try getting the axes given an == (not is) polar projection
24162414
with pytest.warns(

‎lib/matplotlib/tests/test_figure.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_figure.py
+1-3Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,7 @@ def test_gca():
182182
# with a spec of 111.
183183
with pytest.warns(
184184
MatplotlibDeprecationWarning,
185-
match=r'Calling gca\(\) with keyword arguments is deprecated'), \
186-
pytest.raises(
187-
ValueError, match=r'arguments passed to gca\(\) did not match'):
185+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
188186
# Changing the projection will raise an exception
189187
fig.gca(polar=True)
190188

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.