Skip to content

Commit 178278a

Browse files
committed
Let alt/twinx/y accept format() kwargs like dualx/y
1 parent ae31d37 commit 178278a

File tree

1 file changed

+87
-48
lines changed

1 file changed

+87
-48
lines changed

proplot/axes.py

Lines changed: 87 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,16 +1617,16 @@ def number(self, num):
16171617

16181618

16191619
# TODO: More systematic approach?
1620-
dualxy_kwargs = (
1620+
_twin_kwargs = (
16211621
'label', 'locator', 'formatter', 'ticks', 'ticklabels',
16221622
'minorlocator', 'minorticks', 'tickminor',
16231623
'ticklen', 'tickrange', 'tickdir', 'ticklabeldir', 'tickrotation',
1624-
'bounds', 'margin', 'color', 'grid', 'gridminor',
1624+
'bounds', 'margin', 'color', 'linewidth', 'grid', 'gridminor', 'gridcolor',
16251625
'locator_kw', 'formatter_kw', 'minorlocator_kw', 'label_kw',
16261626
)
16271627

1628-
dualxy_descrip = """
1629-
Makes a secondary *%(x)s* axis for denoting equivalent *%(x)s*
1628+
_dual_doc = """
1629+
Return a secondary *%(x)s* axis for denoting equivalent *%(x)s*
16301630
coordinates in *alternate units*.
16311631
16321632
Parameters
@@ -1641,11 +1641,20 @@ def number(self, num):
16411641
Prepended with ``'%(x)s'`` and passed to `Axes.format`.
16421642
"""
16431643

1644-
altxy_descrip = """
1645-
Alias and more intuitive name for `~XYAxes.twin%(y)s`.
1646-
The matplotlib `~matplotlib.axes.Axes.twin%(y)s` function
1647-
generates two *%(x)s* axes with a shared ("twin") *%(y)s* axis.
1648-
Enforces the following settings.
1644+
_alt_doc = """
1645+
Return an axes in the same location as this one but whose %(x)s axis is on
1646+
the %(x2)s. This is an alias and more intuitive name for
1647+
`~CartesianAxes.twin%(y)s`, which generates two *%(x)s* axes with
1648+
a shared ("twin") *%(y)s* axes.
1649+
1650+
Parameters
1651+
----------
1652+
%(args)s : optional
1653+
Prepended with ``'%(x)s'`` and passed to `Axes.format`.
1654+
1655+
Note
1656+
----
1657+
This function enforces the following settngs.
16491658
16501659
* Places the old *%(x)s* axis on the %(x1)s and the new *%(x)s* axis
16511660
on the %(x2)s.
@@ -1655,11 +1664,20 @@ def number(self, num):
16551664
according to the visible spine positions.
16561665
* Locks the old and new *%(y)s* axis limits and scales, and makes the new
16571666
%(y)s axis labels invisible.
1667+
16581668
"""
16591669

1660-
twinxy_descrip = """
1661-
Mimics matplotlib's `~matplotlib.axes.Axes.twin%(y)s`.
1662-
Enforces the following settings.
1670+
_twin_doc = """
1671+
Mimics the builtin `~matplotlib.axes.Axes.twin%(y)s` method.
1672+
1673+
Parameters
1674+
----------
1675+
%(args)s : optional
1676+
Prepended with ``'%(x)s'`` and passed to `Axes.format`.
1677+
1678+
Note
1679+
----
1680+
This function enforces the following settngs.
16631681
16641682
* Places the old *%(x)s* axis on the %(x1)s and the new *%(x)s* axis
16651683
on the %(x2)s.
@@ -1672,28 +1690,24 @@ def number(self, num):
16721690
"""
16731691

16741692

1675-
def _parse_dualxy_args(x, kwargs):
1676-
"""Detect `~XYAxes.format` arguments with the leading ``x`` or ``y``
1677-
removed. Translate to valid `~XYAxes.format` arguments."""
1678-
kwargs_bad = {}
1679-
for key in (*kwargs.keys(),):
1680-
value = kwargs.pop(key)
1681-
if key[0] == x and key[1:] in dualxy_kwargs:
1693+
def _parse_alt(x, kwargs):
1694+
"""Interpret keyword args passed to all "twin axis" methods so they
1695+
can be passed to Axes.format."""
1696+
kw_bad, kw_out = {}, {}
1697+
for key, value in kwargs.items():
1698+
if key in _twin_kwargs:
1699+
kw_out[x + key] = value
1700+
elif key[0] == x and key[1:] in _twin_kwargs:
16821701
_warn_proplot(
1683-
f'dual{x}() keyword arg {key!r} is deprecated. '
1684-
f'Use {key[1:]!r} instead.'
1685-
)
1686-
kwargs[key] = value
1687-
elif key in dualxy_kwargs:
1688-
kwargs[x + key] = value
1702+
f'Twin axis keyword arg {key!r} is deprecated. '
1703+
f'Use {key[1:]!r} instead.')
1704+
kw_out[key] = value
16891705
elif key in RC_NODOTSNAMES:
1690-
kwargs[key] = value
1706+
kw_out[key] = value
16911707
else:
1692-
kwargs_bad[key] = value
1693-
if kwargs_bad:
1694-
raise TypeError(
1695-
f'dual{x}() got unexpected keyword argument(s): {kwargs_bad}'
1696-
)
1708+
kw_bad[key] = value
1709+
if kw_bad:
1710+
raise TypeError(f'Unexpected keyword argument(s): {kw_bad!r}')
16971711
return kwargs
16981712

16991713

@@ -1757,7 +1771,7 @@ def __init__(self, *args, **kwargs):
17571771
self._dualx_cache = None
17581772

17591773
def _altx_overrides(self):
1760-
"""Applies alternate *x* axis overrides."""
1774+
"""Apply alternate *x* axis overrides."""
17611775
# Unlike matplotlib API, we strong arm user into certain twin axes
17621776
# settings... doesn't really make sense to have twin axes without this
17631777
if self._altx_child is not None: # altx was called on this axes
@@ -1777,7 +1791,7 @@ def _altx_overrides(self):
17771791
self.patch.set_visible(False)
17781792

17791793
def _alty_overrides(self):
1780-
"""Applies alternate *y* axis overrides."""
1794+
"""Apply alternate *y* axis overrides."""
17811795
if self._alty_child is not None:
17821796
self._shared_x_axes.join(self, self._alty_child)
17831797
self.spines['right'].set_visible(False)
@@ -1878,6 +1892,19 @@ def _hide_labels(self):
18781892
# Enforce no minor ticks labels. TODO: Document?
18791893
axis.set_minor_formatter(mticker.NullFormatter())
18801894

1895+
def _make_twin_axes(self, *args, **kwargs):
1896+
"""Return a twin of this axes. This is used for twinx and twiny and was
1897+
copied from matplotlib in case the private API changes."""
1898+
# Typically, SubplotBase._make_twin_axes is called instead of this.
1899+
# There is also an override in axes_grid1/axes_divider.py.
1900+
if 'sharex' in kwargs and 'sharey' in kwargs:
1901+
raise ValueError('Twinned Axes may share only one axis.')
1902+
ax2 = self.figure.add_axes(self.get_position(True), *args, **kwargs)
1903+
self.set_adjustable('datalim')
1904+
ax2.set_adjustable('datalim')
1905+
self._twinned_axes.join(self, ax2)
1906+
return ax2
1907+
18811908
def _sharex_setup(self, sharex, level):
18821909
"""Sets up shared axes. The input is the 'parent' axes, from which
18831910
this one will draw its properties."""
@@ -2574,8 +2601,8 @@ def _grid_dict(grid):
25742601
self.set_aspect(aspect)
25752602
super().format(**kwargs)
25762603

2577-
def altx(self):
2578-
# TODO: Accept format **kwargs? Is this already in #50?
2604+
def altx(self, **kwargs):
2605+
"""Docstring is replaced below."""
25792606
# Cannot wrap twiny() because we want to use XYAxes, not
25802607
# matplotlib Axes. Instead use hidden method _make_twin_axes.
25812608
# See https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/axes/_subplots.py # noqa
@@ -2591,9 +2618,11 @@ def altx(self):
25912618
ax._altx_overrides()
25922619
self.add_child_axes(ax) # to facilitate tight layout
25932620
self.figure._axstack.remove(ax) # or gets drawn twice!
2621+
ax.format(**_parse_alt('x', kwargs))
25942622
return ax
25952623

2596-
def alty(self):
2624+
def alty(self, **kwargs):
2625+
"""Docstring is replaced below."""
25972626
if self._alty_child or self._alty_parent:
25982627
raise RuntimeError('No more than *two* twin axes are allowed.')
25992628
with self.figure._authorize_add_subplot():
@@ -2606,23 +2635,26 @@ def alty(self):
26062635
ax._alty_overrides()
26072636
self.add_child_axes(ax) # to facilitate tight layout
26082637
self.figure._axstack.remove(ax) # or gets drawn twice!
2638+
ax.format(**_parse_alt('y', kwargs))
26092639
return ax
26102640

26112641
def dualx(self, arg, **kwargs):
2642+
"""Docstring is replaced below."""
26122643
# NOTE: Matplotlib 3.1 has a 'secondary axis' feature. For the time
26132644
# being, our version is more robust (see FuncScale) and simpler, since
26142645
# we do not create an entirely separate _SecondaryAxis class.
26152646
ax = self.altx()
26162647
self._dualx_arg = arg
26172648
self._dualx_overrides()
2618-
ax.format(**_parse_dualxy_args('x', kwargs))
2649+
ax.format(**kwargs)
26192650
return ax
26202651

26212652
def dualy(self, arg, **kwargs):
2653+
"""Docstring is replaced below."""
26222654
ax = self.alty()
26232655
self._dualy_arg = arg
26242656
self._dualy_overrides()
2625-
ax.format(**_parse_dualxy_args('y', kwargs))
2657+
ax.format(**kwargs)
26262658
return ax
26272659

26282660
def draw(self, renderer=None, *args, **kwargs):
@@ -2653,32 +2685,39 @@ def get_tightbbox(self, renderer, *args, **kwargs):
26532685
return super().get_tightbbox(renderer, *args, **kwargs)
26542686

26552687
def twinx(self):
2688+
"""Docstring is replaced below."""
26562689
return self.alty()
26572690

26582691
def twiny(self):
2692+
"""Docstring is replaced below."""
26592693
return self.altx()
26602694

2661-
altx.__doc__ = altxy_descrip % {
2695+
# Add documentation
2696+
altx.__doc__ = _alt_doc % {
26622697
'x': 'x', 'x1': 'bottom', 'x2': 'top',
26632698
'y': 'y', 'y1': 'left', 'y2': 'right',
2699+
'args': ', '.join(_twin_kwargs),
26642700
}
2665-
alty.__doc__ = altxy_descrip % {
2701+
alty.__doc__ = _alt_doc % {
26662702
'x': 'y', 'x1': 'left', 'x2': 'right',
26672703
'y': 'x', 'y1': 'bottom', 'y2': 'top',
2704+
'args': ', '.join(_twin_kwargs),
26682705
}
2669-
dualx.__doc__ = dualxy_descrip % {
2670-
'x': 'x', 'args': ', '.join(dualxy_kwargs)
2671-
}
2672-
dualy.__doc__ = dualxy_descrip % {
2673-
'x': 'y', 'args': ', '.join(dualxy_kwargs)
2674-
}
2675-
twinx.__doc__ = twinxy_descrip % {
2706+
twinx.__doc__ = _twin_doc % {
26762707
'x': 'y', 'x1': 'left', 'x2': 'right',
26772708
'y': 'x', 'y1': 'bottom', 'y2': 'top',
2709+
'args': ', '.join(_twin_kwargs),
26782710
}
2679-
twiny.__doc__ = twinxy_descrip % {
2711+
twiny.__doc__ = _twin_doc % {
26802712
'x': 'x', 'x1': 'bottom', 'x2': 'top',
26812713
'y': 'y', 'y1': 'left', 'y2': 'right',
2714+
'args': ', '.join(_twin_kwargs),
2715+
}
2716+
dualx.__doc__ = _dual_doc % {
2717+
'x': 'x', 'args': ', '.join(_twin_kwargs)
2718+
}
2719+
dualy.__doc__ = _dual_doc % {
2720+
'x': 'y', 'args': ', '.join(_twin_kwargs)
26822721
}
26832722

26842723

0 commit comments

Comments
 (0)