Skip to content

Commit 623ed98

Browse files
committed
Make inset_axes and secondary_axis picklable.
... by making the locator a class, instead of a nested function. Also, reuse the inset_locator machinery in secondary_axis, instead of duplicating it there.
1 parent 57c8baa commit 623ed98

File tree

3 files changed

+32
-52
lines changed

3 files changed

+32
-52
lines changed

lib/matplotlib/axes/_axes.py

+21-24
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,29 @@
3737
_log = logging.getLogger(__name__)
3838

3939

40-
def _make_inset_locator(bounds, trans, parent):
40+
class _InsetLocator:
4141
"""
42-
Helper function to locate inset axes, used in
43-
`.Axes.inset_axes`.
42+
Axes locator for `.Axes.inset_axes`.
4443
45-
A locator gets used in `Axes.set_aspect` to override the default
46-
locations... It is a function that takes an axes object and
47-
a renderer and tells `set_aspect` where it is to be placed.
48-
49-
Here *rect* is a rectangle [l, b, w, h] that specifies the
50-
location for the axes in the transform given by *trans* on the
51-
*parent*.
44+
The locater is a callable object used in `.Axes.set_aspect` to compute the
45+
axes location depending on the renderer.
5246
"""
53-
_bounds = mtransforms.Bbox.from_bounds(*bounds)
54-
_trans = trans
55-
_parent = parent
5647

57-
def inset_locator(ax, renderer):
58-
bbox = _bounds
59-
bb = mtransforms.TransformedBbox(bbox, _trans)
60-
tr = _parent.figure.transFigure.inverted()
61-
bb = mtransforms.TransformedBbox(bb, tr)
62-
return bb
48+
def __init__(self, bounds, transform):
49+
"""
50+
*bounds* (a ``[l, b, w, h]`` rectangle) and *transform* together
51+
specify the position of the inset axes.
52+
"""
53+
self._bounds = bounds
54+
self._transform = transform
6355

64-
return inset_locator
56+
def __call__(self, ax, renderer):
57+
# Subtracting transFigure will typically rely on inverted(), freezing
58+
# the transform; thus, this needs to be delayed until draw time as
59+
# transFigure may otherwise change after this is evaluated.
60+
return mtransforms.TransformedBbox(
61+
mtransforms.Bbox.from_bounds(*self._bounds),
62+
self._transform - ax.figure.transFigure)
6563

6664

6765
# The axes module contains all the wrappers to plotting functions.
@@ -468,10 +466,9 @@ def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs):
468466
kwargs.setdefault('label', 'inset_axes')
469467

470468
# This puts the rectangle into figure-relative coordinates.
471-
inset_locator = _make_inset_locator(bounds, transform, self)
472-
bb = inset_locator(None, None)
473-
474-
inset_ax = Axes(self.figure, bb.bounds, zorder=zorder, **kwargs)
469+
inset_locator = _InsetLocator(bounds, transform)
470+
bounds = inset_locator(self, None).bounds
471+
inset_ax = Axes(self.figure, bounds, zorder=zorder, **kwargs)
475472
# this locator lets the axes move if in data coordinates.
476473
# it gets called in `ax.apply_aspect() (of all places)
477474
inset_ax.set_axes_locator(inset_locator)

lib/matplotlib/axes/_secondary_axes.py

+4-28
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,10 @@
44
import matplotlib.docstring as docstring
55
import matplotlib.ticker as mticker
66
import matplotlib.transforms as mtransforms
7+
from matplotlib.axes import _axes
78
from matplotlib.axes._base import _AxesBase
89

910

10-
def _make_secondary_locator(rect, parent):
11-
"""
12-
Helper function to locate the secondary axes.
13-
14-
A locator gets used in `Axes.set_aspect` to override the default
15-
locations... It is a function that takes an axes object and
16-
a renderer and tells `set_aspect` where it is to be placed.
17-
18-
This locator make the transform be in axes-relative co-coordinates
19-
because that is how we specify the "location" of the secondary axes.
20-
21-
Here *rect* is a rectangle [l, b, w, h] that specifies the
22-
location for the axes in the transform given by *trans* on the
23-
*parent*.
24-
"""
25-
_rect = mtransforms.Bbox.from_bounds(*rect)
26-
def secondary_locator(ax, renderer):
27-
# delay evaluating transform until draw time because the
28-
# parent transform may have changed (i.e. if window reesized)
29-
bb = mtransforms.TransformedBbox(_rect, parent.transAxes)
30-
tr = parent.figure.transFigure.inverted()
31-
bb = mtransforms.TransformedBbox(bb, tr)
32-
return bb
33-
34-
return secondary_locator
35-
36-
3711
class SecondaryAxis(_AxesBase):
3812
"""
3913
General class to hold a Secondary_X/Yaxis.
@@ -137,11 +111,13 @@ def set_location(self, location):
137111
self._loc = location
138112

139113
if self._orientation == 'x':
114+
# An x-secondary axes is like an inset axes from x = 0 to x = 1 and
115+
# from y = pos to y = pos + eps, in the parent's transAxes coords.
140116
bounds = [0, self._pos, 1., 1e-10]
141117
else:
142118
bounds = [self._pos, 0, 1e-10, 1]
143119

144-
secondary_locator = _make_secondary_locator(bounds, self._parent)
120+
secondary_locator = _axes._InsetLocator(bounds, self._parent.transAxes)
145121

146122
# this locator lets the axes move in the parent axes coordinates.
147123
# so it never needs to know where the parent is explicitly in

lib/matplotlib/tests/test_pickle.py

+7
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,13 @@ def test_shared():
193193
assert fig.axes[1].get_xlim() == (10, 20)
194194

195195

196+
def test_inset_and_secondary():
197+
fig, ax = plt.subplots()
198+
ax.inset_axes([.1, .1, .3, .3])
199+
ax.secondary_xaxis("top", functions=(np.square, np.sqrt))
200+
pickle.loads(pickle.dumps(fig))
201+
202+
196203
@pytest.mark.parametrize("cmap", cm._cmap_registry.values())
197204
def test_cmap(cmap):
198205
pickle.dumps(cmap)

0 commit comments

Comments
 (0)