1
- from collections .abc import MutableSequence
1
+ from collections .abc import Iterable , MutableSequence
2
2
from contextlib import ExitStack
3
3
import functools
4
4
import inspect
18
18
import matplotlib .collections as mcoll
19
19
import matplotlib .colors as mcolors
20
20
import matplotlib .font_manager as font_manager
21
+ from matplotlib .gridspec import SubplotSpec
21
22
import matplotlib .image as mimage
22
23
import matplotlib .lines as mlines
23
24
import matplotlib .patches as mpatches
@@ -571,8 +572,8 @@ def __str__(self):
571
572
return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})" .format (
572
573
type (self ).__name__ , self ._position .bounds )
573
574
574
- def __init__ (self , fig , rect ,
575
- * ,
575
+ def __init__ (self , fig ,
576
+ * args ,
576
577
facecolor = None , # defaults to rc axes.facecolor
577
578
frameon = True ,
578
579
sharex = None , # use Axes instance's xaxis info
@@ -591,9 +592,18 @@ def __init__(self, fig, rect,
591
592
fig : `~matplotlib.figure.Figure`
592
593
The Axes is built in the `.Figure` *fig*.
593
594
594
- rect : tuple (left, bottom, width, height).
595
- The Axes is built in the rectangle *rect*. *rect* is in
596
- `.Figure` coordinates.
595
+ *args
596
+ ``*args`` can be a single ``(left, bottom, width, height)``
597
+ rectangle or a single `.Bbox`. This specifies the rectangle (in
598
+ figure coordinates) where the Axes is positioned.
599
+
600
+ ``*args`` can also consist of three numbers or a single three-digit
601
+ number; in the latter case, the digits are considered as
602
+ independent numbers. The numbers are interpreted as ``(nrows,
603
+ ncols, index)``: ``(nrows, ncols)`` specifies the size of an array
604
+ of subplots, and ``index`` is the 1-based index of the subplot
605
+ being created. Finally, ``*args`` can also directly be a
606
+ `.SubplotSpec` instance.
597
607
598
608
sharex, sharey : `~.axes.Axes`, optional
599
609
The x or y `~.matplotlib.axis` is shared with the x or
@@ -618,10 +628,21 @@ def __init__(self, fig, rect,
618
628
"""
619
629
620
630
super ().__init__ ()
621
- if isinstance (rect , mtransforms .Bbox ):
622
- self ._position = rect
631
+ if "rect" in kwargs :
632
+ if args :
633
+ raise TypeError (
634
+ "'rect' cannot be used together with positional arguments" )
635
+ rect = kwargs .pop ("rect" )
636
+ _api .check_isinstance ((mtransforms .Bbox , Iterable ), rect = rect )
637
+ args = (rect ,)
638
+ subplotspec = None
639
+ if len (args ) == 1 and isinstance (args [0 ], mtransforms .Bbox ):
640
+ self ._position = args [0 ]
641
+ elif len (args ) == 1 and np .iterable (args [0 ]):
642
+ self ._position = mtransforms .Bbox .from_bounds (* args [0 ])
623
643
else :
624
- self ._position = mtransforms .Bbox .from_bounds (* rect )
644
+ self ._position = self ._originalPosition = mtransforms .Bbox .unit ()
645
+ subplotspec = SubplotSpec ._from_subplot_args (fig , args )
625
646
if self ._position .width < 0 or self ._position .height < 0 :
626
647
raise ValueError ('Width and height specified must be non-negative' )
627
648
self ._originalPosition = self ._position .frozen ()
@@ -634,8 +655,16 @@ def __init__(self, fig, rect,
634
655
self ._sharey = sharey
635
656
self .set_label (label )
636
657
self .set_figure (fig )
658
+ # The subplotspec needs to be set after the figure (so that
659
+ # figure-level subplotpars are taken into account), but the figure
660
+ # needs to be set after self._position is initialized.
661
+ if subplotspec :
662
+ self .set_subplotspec (subplotspec )
663
+ else :
664
+ self ._subplotspec = None
637
665
self .set_box_aspect (box_aspect )
638
666
self ._axes_locator = None # Optionally set via update(kwargs).
667
+
639
668
# placeholder for any colorbars added that use this Axes.
640
669
# (see colorbar.py):
641
670
self ._colorbars = []
@@ -753,6 +782,19 @@ def __repr__(self):
753
782
fields += [f"{ name } label={ axis .get_label ().get_text ()!r} " ]
754
783
return f"<{ self .__class__ .__name__ } : " + ", " .join (fields ) + ">"
755
784
785
+ def get_subplotspec (self ):
786
+ """Return the `.SubplotSpec` associated with the subplot, or None."""
787
+ return self ._subplotspec
788
+
789
+ def set_subplotspec (self , subplotspec ):
790
+ """Set the `.SubplotSpec`. associated with the subplot."""
791
+ self ._subplotspec = subplotspec
792
+ self ._set_position (subplotspec .get_position (self .figure ))
793
+
794
+ def get_gridspec (self ):
795
+ """Return the `.GridSpec` associated with the subplot, or None."""
796
+ return self ._subplotspec .get_gridspec () if self ._subplotspec else None
797
+
756
798
@_api .delete_parameter ("3.6" , "args" )
757
799
@_api .delete_parameter ("3.6" , "kwargs" )
758
800
def get_window_extent (self , renderer = None , * args , ** kwargs ):
@@ -4460,17 +4502,23 @@ def get_tightbbox(self, renderer=None, call_axes_locator=True,
4460
4502
4461
4503
def _make_twin_axes (self , * args , ** kwargs ):
4462
4504
"""Make a twinx Axes of self. This is used for twinx and twiny."""
4463
- # Typically, SubplotBase._make_twin_axes is called instead of this.
4464
4505
if 'sharex' in kwargs and 'sharey' in kwargs :
4465
- raise ValueError ("Twinned Axes may share only one axis" )
4466
- ax2 = self .figure .add_axes (
4467
- self .get_position (True ), * args , ** kwargs ,
4468
- axes_locator = _TransformedBoundsLocator (
4469
- [0 , 0 , 1 , 1 ], self .transAxes ))
4506
+ # The following line is added in v2.2 to avoid breaking Seaborn,
4507
+ # which currently uses this internal API.
4508
+ if kwargs ["sharex" ] is not self and kwargs ["sharey" ] is not self :
4509
+ raise ValueError ("Twinned Axes may share only one axis" )
4510
+ ss = self .get_subplotspec ()
4511
+ if ss :
4512
+ twin = self .figure .add_subplot (ss , * args , ** kwargs )
4513
+ else :
4514
+ twin = self .figure .add_axes (
4515
+ self .get_position (True ), * args , ** kwargs ,
4516
+ axes_locator = _TransformedBoundsLocator (
4517
+ [0 , 0 , 1 , 1 ], self .transAxes ))
4470
4518
self .set_adjustable ('datalim' )
4471
- ax2 .set_adjustable ('datalim' )
4472
- self ._twinned_axes .join (self , ax2 )
4473
- return ax2
4519
+ twin .set_adjustable ('datalim' )
4520
+ self ._twinned_axes .join (self , twin )
4521
+ return twin
4474
4522
4475
4523
def twinx (self ):
4476
4524
"""
@@ -4538,3 +4586,56 @@ def get_shared_x_axes(self):
4538
4586
def get_shared_y_axes (self ):
4539
4587
"""Return an immutable view on the shared y-axes Grouper."""
4540
4588
return cbook .GrouperView (self ._shared_axes ["y" ])
4589
+
4590
+ def label_outer (self ):
4591
+ """
4592
+ Only show "outer" labels and tick labels.
4593
+
4594
+ x-labels are only kept for subplots on the last row (or first row, if
4595
+ labels are on the top side); y-labels only for subplots on the first
4596
+ column (or last column, if labels are on the right side).
4597
+ """
4598
+ self ._label_outer_xaxis (check_patch = False )
4599
+ self ._label_outer_yaxis (check_patch = False )
4600
+
4601
+ def _label_outer_xaxis (self , * , check_patch ):
4602
+ # see documentation in label_outer.
4603
+ if check_patch and not isinstance (self .patch , mpl .patches .Rectangle ):
4604
+ return
4605
+ ss = self .get_subplotspec ()
4606
+ if not ss :
4607
+ return
4608
+ label_position = self .xaxis .get_label_position ()
4609
+ if not ss .is_first_row (): # Remove top label/ticklabels/offsettext.
4610
+ if label_position == "top" :
4611
+ self .set_xlabel ("" )
4612
+ self .xaxis .set_tick_params (which = "both" , labeltop = False )
4613
+ if self .xaxis .offsetText .get_position ()[1 ] == 1 :
4614
+ self .xaxis .offsetText .set_visible (False )
4615
+ if not ss .is_last_row (): # Remove bottom label/ticklabels/offsettext.
4616
+ if label_position == "bottom" :
4617
+ self .set_xlabel ("" )
4618
+ self .xaxis .set_tick_params (which = "both" , labelbottom = False )
4619
+ if self .xaxis .offsetText .get_position ()[1 ] == 0 :
4620
+ self .xaxis .offsetText .set_visible (False )
4621
+
4622
+ def _label_outer_yaxis (self , * , check_patch ):
4623
+ # see documentation in label_outer.
4624
+ if check_patch and not isinstance (self .patch , mpl .patches .Rectangle ):
4625
+ return
4626
+ ss = self .get_subplotspec ()
4627
+ if not ss :
4628
+ return
4629
+ label_position = self .yaxis .get_label_position ()
4630
+ if not ss .is_first_col (): # Remove left label/ticklabels/offsettext.
4631
+ if label_position == "left" :
4632
+ self .set_ylabel ("" )
4633
+ self .yaxis .set_tick_params (which = "both" , labelleft = False )
4634
+ if self .yaxis .offsetText .get_position ()[0 ] == 0 :
4635
+ self .yaxis .offsetText .set_visible (False )
4636
+ if not ss .is_last_col (): # Remove right label/ticklabels/offsettext.
4637
+ if label_position == "right" :
4638
+ self .set_ylabel ("" )
4639
+ self .yaxis .set_tick_params (which = "both" , labelright = False )
4640
+ if self .yaxis .offsetText .get_position ()[0 ] == 1 :
4641
+ self .yaxis .offsetText .set_visible (False )
0 commit comments