From 1eb132309ac5b53aeeb10942a1685ef78ba5631a Mon Sep 17 00:00:00 2001 From: Ruth Comer Date: Sat, 14 Jan 2023 07:48:22 +0000 Subject: [PATCH] ENH: pad_inches='layout' for savefig --- .../next_whats_new/savefig_bbox_layout.rst | 10 ++++++++ lib/matplotlib/backend_bases.py | 24 +++++++++++++----- lib/matplotlib/figure.py | 7 +++-- lib/matplotlib/layout_engine.py | 4 +-- .../bbox_inches_tight_layout.png | Bin 0 -> 1952 bytes lib/matplotlib/tests/test_bbox_tight.py | 16 ++++++++++++ lib/matplotlib/transforms.py | 19 +++++++++++--- 7 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 doc/users/next_whats_new/savefig_bbox_layout.rst create mode 100644 lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_layout.png diff --git a/doc/users/next_whats_new/savefig_bbox_layout.rst b/doc/users/next_whats_new/savefig_bbox_layout.rst new file mode 100644 index 000000000000..bf2d2bb72f90 --- /dev/null +++ b/doc/users/next_whats_new/savefig_bbox_layout.rst @@ -0,0 +1,10 @@ +pad_inches="layout" for savefig +------------------------------- + +When using constrained or compressed layout, + +.. code-block:: python + + savefig(filename, bbox_inches="tight", pad_inches="layout") + +will now use the padding sizes defined on the layout engine. diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index c573c369e9e3..b54f6fed167b 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -48,6 +48,7 @@ from matplotlib._pylab_helpers import Gcf from matplotlib.backend_managers import ToolManager from matplotlib.cbook import _setattr_cm +from matplotlib.layout_engine import ConstrainedLayoutEngine from matplotlib.path import Path from matplotlib.texmanager import TexManager from matplotlib.transforms import Affine2D @@ -2273,8 +2274,11 @@ def print_figure( Bounding box in inches: only the given portion of the figure is saved. If 'tight', try to figure out the tight bbox of the figure. - pad_inches : float, default: :rc:`savefig.pad_inches` - Amount of padding around the figure when *bbox_inches* is 'tight'. + pad_inches : float or 'layout', default: :rc:`savefig.pad_inches` + Amount of padding in inches around the figure when bbox_inches is + 'tight'. If 'layout' use the padding from the constrained or + compressed layout engine; ignored if one of those engines is not in + use. bbox_extra_artists : list of `~matplotlib.artist.Artist`, optional A list of extra artists that will be considered when the @@ -2324,8 +2328,8 @@ def print_figure( if bbox_inches is None: bbox_inches = rcParams['savefig.bbox'] - if (self.figure.get_layout_engine() is not None or - bbox_inches == "tight"): + layout_engine = self.figure.get_layout_engine() + if layout_engine is not None or bbox_inches == "tight": # we need to trigger a draw before printing to make sure # CL works. "tight" also needs a draw to get the right # locations: @@ -2341,9 +2345,15 @@ def print_figure( if bbox_inches == "tight": bbox_inches = self.figure.get_tightbbox( renderer, bbox_extra_artists=bbox_extra_artists) - if pad_inches is None: - pad_inches = rcParams['savefig.pad_inches'] - bbox_inches = bbox_inches.padded(pad_inches) + if (isinstance(layout_engine, ConstrainedLayoutEngine) and + pad_inches == "layout"): + h_pad = layout_engine.get()["h_pad"] + w_pad = layout_engine.get()["w_pad"] + else: + if pad_inches in [None, "layout"]: + pad_inches = rcParams['savefig.pad_inches'] + h_pad = w_pad = pad_inches + bbox_inches = bbox_inches.padded(w_pad, h_pad) # call adjust_bbox to save only the given area restore_bbox = _tight_bbox.adjust_bbox( diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 7b273cf9fb81..e911a037d818 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -3257,8 +3257,11 @@ def savefig(self, fname, *, transparent=None, **kwargs): Bounding box in inches: only the given portion of the figure is saved. If 'tight', try to figure out the tight bbox of the figure. - pad_inches : float, default: :rc:`savefig.pad_inches` - Amount of padding around the figure when bbox_inches is 'tight'. + pad_inches : float or 'layout', default: :rc:`savefig.pad_inches` + Amount of padding in inches around the figure when bbox_inches is + 'tight'. If 'layout' use the padding from the constrained or + compressed layout engine; ignored if one of those engines is not in + use. facecolor : color or 'auto', default: :rc:`savefig.facecolor` The facecolor of the figure. If 'auto', use the current figure diff --git a/lib/matplotlib/layout_engine.py b/lib/matplotlib/layout_engine.py index 49b3f1dd125f..185be9857abb 100644 --- a/lib/matplotlib/layout_engine.py +++ b/lib/matplotlib/layout_engine.py @@ -203,7 +203,7 @@ def __init__(self, *, h_pad=None, w_pad=None, Parameters ---------- h_pad, w_pad : float - Padding around the axes elements in figure-normalized units. + Padding around the axes elements in inches. Default to :rc:`figure.constrained_layout.h_pad` and :rc:`figure.constrained_layout.w_pad`. hspace, wspace : float @@ -261,7 +261,7 @@ def set(self, *, h_pad=None, w_pad=None, Parameters ---------- h_pad, w_pad : float - Padding around the axes elements in figure-normalized units. + Padding around the axes elements in inches. Default to :rc:`figure.constrained_layout.h_pad` and :rc:`figure.constrained_layout.w_pad`. hspace, wspace : float diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_layout.png b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_layout.png new file mode 100644 index 0000000000000000000000000000000000000000..657eaed42267f285b9b05a18dbbaeea6e3f36be2 GIT binary patch literal 1952 zcmeAS@N?(olHy`uVBq!ia0y~yU{nC&8V)v~NdEaW9w6mg;u=vBoS#-wo>-L1;G0-d zkdt4MlbNJYmReMtnV)B@XRc?Umy%j$Xl!n%ot|iBZeo~bVVahxqmWTjQc!HAub&Io zq?ccmuAjuD;|4UCv%n*=n1O*m5ri36*e}myU|{>{>EaktG3V_K!|Xe!q#7O;8mx$B zy~O(DPssFtQ!^jgebkyd>qS_4|65a;9pC5GZv0-Ez{RUmbL&y6iTUq+@0k@otdwKe z5y!-m!oqQaLtuiSf`YPxgIhyG4+EnlBa_j9NzA-`UHbO6-rU=M=j;Fdk+iS>ClkM~ zM$+8eTwIp#hsC#A`Th0(<>L3($g?Hl?|)Z3 z`^x#E!lrNc?%f}M{IK9_Kg{qWf6Z&FX{nO_{{G4Ho*N#Mo}X~pq&IWcxw;w)85WKo z^KJK^D&*uHY{7vN7dvbBZp^)XW82#=d)B>nWoKG<{3hp%hYu6ae6EQ)y-B2)G5kT= z{qKK&9*b6AkQ#aHV}(rn;fIfyndA@eS_kyBxUS`Imf6cPeJ+=LeAw{%v5?fDig^^T z0@Gvh@2ce6+khT^Ss8o1n~x=?f3xs~!-o&gc>Z(Cv{cP!OzRGS;`(#%c0Y&Hn|dC9 z1jX}Fb{4y~(%9oR=k=qCzHyrQUY@b+<)g!l*N>~344y=e?p0vcd-m5~I>Q>%IJ@@#_vh02a~puk`1s>O8NT*zevTTR*Sq>E z=f!V%@9QgV9 zfvNOz)U>5DxOXstimCJF_pBIoPaE|ee{3PcCoV7WL*iPg>}Q+(87FV5-!Sn_p0%u- zpFKu?`h~$zbYb@@us}sB(}3~TV|Z-Wy6LmIeiZ!6I+kP>TwrnK@t^q0?cZN2@4pSq z=;h_*kFT$ffBgCR`Q#pRyMxOzr7oMy{aZ2bIsf