@@ -614,6 +614,10 @@ def __str__(self):
614614 return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})" .format (
615615 type (self ).__name__ , self ._position .bounds )
616616
617+ def set_zorder (self , level ):
618+ super ().set_zorder (level )
619+ self ._update_twinned_axes_patch_visibility ()
620+
617621 def __init__ (self , fig ,
618622 * args ,
619623 facecolor = None , # defaults to rc axes.facecolor
@@ -3324,6 +3328,7 @@ def set_frame_on(self, b):
33243328 b : bool
33253329 """
33263330 self ._frameon = b
3331+ self ._update_twinned_axes_patch_visibility ()
33273332 self .stale = True
33283333
33293334 def get_axisbelow (self ):
@@ -4705,7 +4710,32 @@ def get_tightbbox(self, renderer=None, *, call_axes_locator=True,
47054710 return mtransforms .Bbox .union (
47064711 [b for b in bb if b .width != 0 or b .height != 0 ])
47074712
4708- def _make_twin_axes (self , * args , ** kwargs ):
4713+ def _update_twinned_axes_patch_visibility (self ):
4714+ """
4715+ Update patch visibility for a group of twinned Axes.
4716+
4717+ Only the bottom-most Axes in the group (lowest zorder, breaking ties by
4718+ creation/insertion order) has a visible background patch.
4719+ """
4720+ if self not in self ._twinned_axes :
4721+ return
4722+ twinned = list (self ._twinned_axes .get_siblings (self ))
4723+ if not twinned :
4724+ return
4725+ fig = self .get_figure (root = False )
4726+ fig_axes = fig .axes if fig is not None else []
4727+ insertion_order = {ax : idx for idx , ax in enumerate (fig_axes )}
4728+
4729+ twinned .sort (
4730+ key = lambda ax : (ax .get_zorder (), insertion_order .get (ax , len (fig_axes )))
4731+ )
4732+ bottom = twinned [0 ]
4733+ for ax in twinned :
4734+ patch = getattr (ax , "patch" , None )
4735+ if patch is not None :
4736+ patch .set_visible ((ax is bottom ) and ax .get_frame_on ())
4737+
4738+ def _make_twin_axes (self , * args , delta_zorder = 0.0 , ** kwargs ):
47094739 """Make a twinx Axes of self. This is used for twinx and twiny."""
47104740 if 'sharex' in kwargs and 'sharey' in kwargs :
47114741 # The following line is added in v2.2 to avoid breaking Seaborn,
@@ -4722,7 +4752,7 @@ def _make_twin_axes(self, *args, **kwargs):
47224752 [0 , 0 , 1 , 1 ], self .transAxes ))
47234753 self .set_adjustable ('datalim' )
47244754 twin .set_adjustable ('datalim' )
4725- twin .set_zorder (self .zorder )
4755+ twin .set_zorder (self .get_zorder () + delta_zorder )
47264756
47274757 self ._twinned_axes .join (self , twin )
47284758
@@ -4739,9 +4769,10 @@ def _make_twin_axes(self, *args, **kwargs):
47394769 twin ._set_position (self .get_position (original = True ), which = "original" )
47404770 twin ._set_position (self .get_position (original = False ), which = "active" )
47414771
4772+ self ._update_twinned_axes_patch_visibility ()
47424773 return twin
47434774
4744- def twinx (self , axes_class = None , ** kwargs ):
4775+ def twinx (self , axes_class = None , * , delta_zorder = 0.0 , * *kwargs ):
47454776 """
47464777 Create a twin Axes sharing the xaxis.
47474778
@@ -4762,6 +4793,12 @@ def twinx(self, axes_class=None, **kwargs):
47624793
47634794 .. versionadded:: 3.11
47644795
4796+ delta_zorder : float, default: 0
4797+ A zorder offset for the twin Axes, relative to the original Axes.
4798+ The twin's zorder is set to ``self.get_zorder() + delta_zorder``.
4799+ By default (*delta_zorder* is 0), the twin has the same zorder as
4800+ the original Axes.
4801+
47654802 kwargs : dict
47664803 The keyword arguments passed to `.Figure.add_subplot` or `.Figure.add_axes`.
47674804
@@ -4779,18 +4816,17 @@ def twinx(self, axes_class=None, **kwargs):
47794816 """
47804817 if axes_class :
47814818 kwargs ["axes_class" ] = axes_class
4782- ax2 = self ._make_twin_axes (sharex = self , ** kwargs )
4819+ ax2 = self ._make_twin_axes (sharex = self , delta_zorder = delta_zorder , ** kwargs )
47834820 ax2 .yaxis .tick_right ()
47844821 ax2 .yaxis .set_label_position ('right' )
47854822 ax2 .yaxis .set_offset_position ('right' )
47864823 ax2 .set_autoscalex_on (self .get_autoscalex_on ())
47874824 self .yaxis .tick_left ()
47884825 ax2 .xaxis .set_visible (False )
4789- ax2 .patch .set_visible (False )
47904826 ax2 .xaxis .units = self .xaxis .units
47914827 return ax2
47924828
4793- def twiny (self , axes_class = None , ** kwargs ):
4829+ def twiny (self , axes_class = None , * , delta_zorder = 0.0 , * *kwargs ):
47944830 """
47954831 Create a twin Axes sharing the yaxis.
47964832
@@ -4811,6 +4847,12 @@ def twiny(self, axes_class=None, **kwargs):
48114847
48124848 .. versionadded:: 3.11
48134849
4850+ delta_zorder : float, default: 0
4851+ A zorder offset for the twin Axes, relative to the original Axes.
4852+ The twin's zorder is set to ``self.get_zorder() + delta_zorder``.
4853+ By default (*delta_zorder* is 0), the twin has the same zorder as
4854+ the original Axes.
4855+
48144856 kwargs : dict
48154857 The keyword arguments passed to `.Figure.add_subplot` or `.Figure.add_axes`.
48164858
@@ -4828,13 +4870,12 @@ def twiny(self, axes_class=None, **kwargs):
48284870 """
48294871 if axes_class :
48304872 kwargs ["axes_class" ] = axes_class
4831- ax2 = self ._make_twin_axes (sharey = self , ** kwargs )
4873+ ax2 = self ._make_twin_axes (sharey = self , delta_zorder = delta_zorder , ** kwargs )
48324874 ax2 .xaxis .tick_top ()
48334875 ax2 .xaxis .set_label_position ('top' )
48344876 ax2 .set_autoscaley_on (self .get_autoscaley_on ())
48354877 self .xaxis .tick_bottom ()
48364878 ax2 .yaxis .set_visible (False )
4837- ax2 .patch .set_visible (False )
48384879 ax2 .yaxis .units = self .yaxis .units
48394880 return ax2
48404881
0 commit comments