diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 108cda04865f..8313d26df190 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -4596,7 +4596,7 @@ def _make_twin_axes(self, *args, **kwargs): self._twinned_axes.join(self, twin) return twin - def twinx(self): + def twinx(self, axes_class=None, **kwargs): """ Create a twin Axes sharing the xaxis. @@ -4606,6 +4606,22 @@ def twinx(self): Axes. To ensure that the tick marks of both y-axes align, see `~matplotlib.ticker.LinearLocator`. + Parameters + ---------- + axes_class : subclass type of `~.axes.Axes`, optional + The `.axes.Axes` subclass that is instantiated. This parameter + is incompatible with *projection* and *polar*. See + :ref:`axisartist_users-guide-index` for examples. + + By default, `~.axes.Axes` is used. + + .. versionadded:: 3.11 + + kwargs : dict + The keyword arguments passed to `.Figure.add_subplot` or `.Figure.add_axes`. + + .. versionadded:: 3.11 + Returns ------- Axes @@ -4616,7 +4632,9 @@ def twinx(self): For those who are 'picking' artists while using twinx, pick events are only called for the artists in the top-most Axes. """ - ax2 = self._make_twin_axes(sharex=self) + if axes_class: + kwargs["axes_class"] = axes_class + ax2 = self._make_twin_axes(sharex=self, **kwargs) ax2.yaxis.tick_right() ax2.yaxis.set_label_position('right') ax2.yaxis.set_offset_position('right') @@ -4627,7 +4645,7 @@ def twinx(self): ax2.xaxis.units = self.xaxis.units return ax2 - def twiny(self): + def twiny(self, axes_class=None, **kwargs): """ Create a twin Axes sharing the yaxis. @@ -4637,6 +4655,22 @@ def twiny(self): To ensure that the tick marks of both x-axes align, see `~matplotlib.ticker.LinearLocator`. + Parameters + ---------- + axes_class : subclass type of `~.axes.Axes`, optional + The `.axes.Axes` subclass that is instantiated. This parameter + is incompatible with *projection* and *polar*. See + :ref:`axisartist_users-guide-index` for examples. + + By default, `~.axes.Axes` is used. + + .. versionadded:: 3.11 + + kwargs : dict + The keyword arguments passed to `.Figure.add_subplot` or `.Figure.add_axes`. + + .. versionadded:: 3.11 + Returns ------- Axes @@ -4647,7 +4681,9 @@ def twiny(self): For those who are 'picking' artists while using twiny, pick events are only called for the artists in the top-most Axes. """ - ax2 = self._make_twin_axes(sharey=self) + if axes_class: + kwargs["axes_class"] = axes_class + ax2 = self._make_twin_axes(sharey=self, **kwargs) ax2.xaxis.tick_top() ax2.xaxis.set_label_position('top') ax2.set_autoscaley_on(self.get_autoscaley_on()) diff --git a/lib/matplotlib/axes/_base.pyi b/lib/matplotlib/axes/_base.pyi index b4926f113564..31208ce23d1e 100644 --- a/lib/matplotlib/axes/_base.pyi +++ b/lib/matplotlib/axes/_base.pyi @@ -385,8 +385,8 @@ class _AxesBase(martist.Artist): bbox_extra_artists: Sequence[Artist] | None = ..., for_layout_only: bool = ... ) -> Bbox | None: ... - def twinx(self) -> Axes: ... - def twiny(self) -> Axes: ... + def twinx(self, axes_class: Axes | None = ..., **kwargs) -> Axes: ... + def twiny(self, axes_class: Axes | None = ..., **kwargs) -> Axes: ... def get_shared_x_axes(self) -> cbook.GrouperView: ... def get_shared_y_axes(self) -> cbook.GrouperView: ... def label_outer(self, remove_inner_ticks: bool = ...) -> None: ... diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 38857e846c55..ecc37c83b55d 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -7532,6 +7532,35 @@ def test_twinx_knows_limits(): assert_array_equal(xtwin.viewLim.intervalx, ax2.viewLim.intervalx) +class SubclassAxes(Axes): + def __init__(self, *args, foo, **kwargs): + super().__init__(*args, **kwargs) + self.foo = foo + + +def test_twinning_with_axes_class(): + """Check that twinx/y(axes_class=...) gives the appropriate class.""" + _, ax = plt.subplots() + twinx = ax.twinx(axes_class=SubclassAxes, foo=1) + assert isinstance(twinx, SubclassAxes) + assert twinx.foo == 1 + twiny = ax.twiny(axes_class=SubclassAxes, foo=2) + assert isinstance(twiny, SubclassAxes) + assert twiny.foo == 2 + + +def test_twinning_default_axes_class(): + """ + Check that the default class for twinx/y() is Axes, + even if the original is an Axes subclass. + """ + _, ax = plt.subplots(subplot_kw=dict(axes_class=SubclassAxes, foo=1)) + twinx = ax.twinx() + assert type(twinx) is Axes + twiny = ax.twiny() + assert type(twiny) is Axes + + def test_zero_linewidth(): # Check that setting a zero linewidth doesn't error plt.plot([0, 1], [0, 1], ls='--', lw=0)