From f3bd630c250c01f1ea56674c500f8333926df793 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 24 Oct 2018 22:05:05 -0400 Subject: [PATCH] Backport PR #12285: FIX: Don't apply tight_layout if axes collapse --- doc/api/api_changes.rst | 8 ++++++ lib/matplotlib/figure.py | 3 ++- lib/matplotlib/gridspec.py | 3 ++- lib/matplotlib/tests/test_tightlayout.py | 16 ++++++++++++ lib/matplotlib/tight_layout.py | 32 ++++++++++++++++-------- 5 files changed, 49 insertions(+), 13 deletions(-) diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 8395cdea6bad..dc3f6477994d 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -36,6 +36,14 @@ This pages lists API changes for the most recent version of Matplotlib. next_api_changes/* +API Changes for 3.0.1 +===================== + +`.tight_layout.auto_adjust_subplotpars` can return ``None`` now if the new +subplotparams will collapse axes to zero width or height. This prevents +``tight_layout`` from being executed. Similarly +`.tight_layout.get_tight_layout_figure` will return None. + API Changes for 3.0.0 ===================== diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 7951b1f5c707..6259b297489c 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -2365,7 +2365,8 @@ def tight_layout(self, renderer=None, pad=1.08, h_pad=None, w_pad=None, kwargs = get_tight_layout_figure( self, self.axes, subplotspec_list, renderer, pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) - self.subplots_adjust(**kwargs) + if kwargs: + self.subplots_adjust(**kwargs) def align_xlabels(self, axs=None): """ diff --git a/lib/matplotlib/gridspec.py b/lib/matplotlib/gridspec.py index 9afe44acd5d1..99b8c99868fb 100644 --- a/lib/matplotlib/gridspec.py +++ b/lib/matplotlib/gridspec.py @@ -341,7 +341,8 @@ def tight_layout(self, figure, renderer=None, kwargs = tight_layout.get_tight_layout_figure( figure, figure.axes, subplotspec_list, renderer, pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) - self.update(**kwargs) + if kwargs: + self.update(**kwargs) class GridSpecFromSubplotSpec(GridSpecBase): diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index 2d143c63d9c4..4d2fbb20980f 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -316,3 +316,19 @@ def test_badsubplotgrid(): with warnings.catch_warnings(record=True) as w: plt.tight_layout() assert len(w) == 1 + + +def test_collapsed(): + # test that if a call to tight_layout will collapes the axes that + # it does not get applied: + fig, ax = plt.subplots(tight_layout=True) + ax.set_xlim([0, 1]) + ax.set_ylim([0, 1]) + + ax.annotate('BIG LONG STRING', xy=(1.25, 2), xytext=(10.5, 1.75),) + p1 = ax.get_position() + with warnings.catch_warnings(record=True) as w: + plt.tight_layout() + p2 = ax.get_position() + assert p1.width == p2.width + assert len(w) == 1 diff --git a/lib/matplotlib/tight_layout.py b/lib/matplotlib/tight_layout.py index 344413cc9b7e..0b3b40e1cd98 100644 --- a/lib/matplotlib/tight_layout.py +++ b/lib/matplotlib/tight_layout.py @@ -38,7 +38,8 @@ def auto_adjust_subplotpars( fig, renderer, nrows_ncols, num1num2_list, subplot_list, ax_bbox_list=None, pad=1.08, h_pad=None, w_pad=None, rect=None): """ - Return a dict of subplot parameters to adjust spacing between subplots. + Return a dict of subplot parameters to adjust spacing between subplots + or ``None`` if resulting axes would have zero height or width. Note that this function ignores geometry information of subplot itself, but uses what is given by the *nrows_ncols* and *num1num2_list* @@ -172,15 +173,15 @@ def auto_adjust_subplotpars( margin_bottom += pad_inches / fig_height_inch if margin_left + margin_right >= 1: - margin_left = 0.4999 - margin_right = 0.4999 - warnings.warn('The left and right margins cannot be made large ' + warnings.warn('Tight layout not applied. The left and right margins ' + 'cannot be made large ' 'enough to accommodate all axes decorations. ') + return None if margin_bottom + margin_top >= 1: - margin_bottom = 0.4999 - margin_top = 0.4999 - warnings.warn('The bottom and top margins cannot be made large ' + warnings.warn('Tight layout not applied. ' + 'The bottom and top margins cannot be made large ' 'enough to accommodate all axes decorations. ') + return None kwargs = dict(left=margin_left, right=1 - margin_right, @@ -195,9 +196,10 @@ def auto_adjust_subplotpars( # axes widths: h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols if h_axes < 0: - warnings.warn('tight_layout cannot make axes width small enough ' + warnings.warn('Tight layout not applied. ' + 'tight_layout cannot make axes width small enough ' 'to accommodate all axes decorations') - kwargs["wspace"] = 0.5 + return None else: kwargs["wspace"] = hspace / h_axes @@ -206,9 +208,10 @@ def auto_adjust_subplotpars( + vpad_inches / fig_height_inch) v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows if v_axes < 0: - warnings.warn('tight_layout cannot make axes height small enough ' + warnings.warn('Tight layout not applied. ' + 'tight_layout cannot make axes height small enough ' 'to accommodate all axes decorations') - kwargs["hspace"] = 0.5 + return None else: kwargs["hspace"] = vspace / v_axes @@ -287,6 +290,13 @@ def get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer, (left, bottom, right, top) rectangle in normalized figure coordinates that the whole subplots area (including labels) will fit into. Defaults to using the entire figure. + + Returns + ------- + subplotspec or None + subplotspec kwargs to be passed to `.Figure.subplots_adjust` or + None if tight_layout could not be accomplished. + """ subplot_list = []