From f7f3bb6079048506613c513231e1bd2a87ebc7d3 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 11 Feb 2022 15:19:28 -0500 Subject: [PATCH 1/2] ENH: add ability to remove layout engine This also adds a "place holder" layout engine to ensure that users can not "go through zero" and change to an incompatible layout engine. Co-authored-by: Jody Klymak --- lib/matplotlib/figure.py | 44 +++++++++++++++++++++++------ lib/matplotlib/layout_engine.py | 24 ++++++++++++++++ lib/matplotlib/tests/test_figure.py | 12 +++++++- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index f8aa99d48a09..f5dfdb20062d 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -35,8 +35,10 @@ from matplotlib.axes import Axes, SubplotBase, subplot_class_factory from matplotlib.gridspec import GridSpec -from matplotlib.layout_engine import (ConstrainedLayoutEngine, - TightLayoutEngine, LayoutEngine) +from matplotlib.layout_engine import ( + ConstrainedLayoutEngine, TightLayoutEngine, LayoutEngine, + PlaceHolderLayoutEngine +) import matplotlib.legend as mlegend from matplotlib.patches import Rectangle from matplotlib.text import Text @@ -2382,7 +2384,9 @@ def _check_layout_engines_compat(self, old, new): If the figure has used the old engine and added a colorbar then the value of colorbar_gridspec must be the same on the new engine. """ - if old is None or old.colorbar_gridspec == new.colorbar_gridspec: + if old is None or new is None: + return True + if old.colorbar_gridspec == new.colorbar_gridspec: return True # colorbar layout different, so check if any colorbars are on the # figure... @@ -2398,15 +2402,29 @@ def set_layout_engine(self, layout=None, **kwargs): Parameters ---------- - layout: {'constrained', 'compressed', 'tight'} or `~.LayoutEngine` - 'constrained' will use `~.ConstrainedLayoutEngine`, - 'compressed' will also use ConstrainedLayoutEngine, but with a - correction that attempts to make a good layout for fixed-aspect - ratio Axes. 'tight' uses `~.TightLayoutEngine`. Users and - libraries can define their own layout engines as well. + layout: {'constrained', 'compressed', 'tight', 'none'} or \ +`LayoutEngine` or None + + - 'constrained' will use `~.ConstrainedLayoutEngine` + - 'compressed' will also use `~.ConstrainedLayoutEngine`, but with + a correction that attempts to make a good layout for fixed-aspect + ratio Axes. + - 'tight' uses `~.TightLayoutEngine` + - 'none' removes layout engine. + + If `None`, the behavior is controlled by :rc:`figure.autolayout` + (which if `True` behaves as if 'tight' were passed) and + :rc:`figure.constrained_layout.use` (which if true behaves as if + 'constrained' were passed). If both are true, + :rc:`figure.autolayout` takes priority. + + Users and libraries can define their own layout engines and pass + the instance directly as well. + kwargs: dict The keyword arguments are passed to the layout engine to set things like padding and margin sizes. Only used if *layout* is a string. + """ if layout is None: if mpl.rcParams['figure.autolayout']: @@ -2423,6 +2441,14 @@ def set_layout_engine(self, layout=None, **kwargs): elif layout == 'compressed': new_layout_engine = ConstrainedLayoutEngine(compress=True, **kwargs) + elif layout == 'none': + if self._layout_engine is not None: + new_layout_engine = PlaceHolderLayoutEngine( + self._layout_engine.adjust_compatible, + self._layout_engine.colorbar_gridspec + ) + else: + new_layout_engine = None elif isinstance(layout, LayoutEngine): new_layout_engine = layout else: diff --git a/lib/matplotlib/layout_engine.py b/lib/matplotlib/layout_engine.py index e0b058e601dd..ee04afef19c7 100644 --- a/lib/matplotlib/layout_engine.py +++ b/lib/matplotlib/layout_engine.py @@ -100,6 +100,30 @@ def execute(self, fig): raise NotImplementedError +class PlaceHolderLayoutEngine(LayoutEngine): + """ + This layout engine does not adjust the figure layout at all. + + The purpose of this `.LayoutEngine` is to act as a place holder when the + user removes a layout engine to ensure an incompatible `.LayoutEngine` can + not be set later. + + Parameters + ---------- + adjust_compatible, colorbar_gridspec : bool + Allow the PlaceHolderLayoutEngine to mirror the behavior of whatever + layout engine it is replacing. + + """ + def __init__(self, adjust_compatible, colorbar_gridspec, **kwargs): + self._adjust_compatible = adjust_compatible + self._colorbar_gridspec = colorbar_gridspec + super().__init__(**kwargs) + + def execute(self, fig): + return + + class TightLayoutEngine(LayoutEngine): """ Implements the ``tight_layout`` geometry management. See diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index ec1a814ebc59..b871d22c2b37 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -17,7 +17,8 @@ from matplotlib.axes import Axes from matplotlib.figure import Figure, FigureBase from matplotlib.layout_engine import (ConstrainedLayoutEngine, - TightLayoutEngine) + TightLayoutEngine, + PlaceHolderLayoutEngine) from matplotlib.ticker import AutoMinorLocator, FixedFormatter, ScalarFormatter import matplotlib.pyplot as plt import matplotlib.dates as mdates @@ -578,6 +579,9 @@ def test_invalid_layouts(): fig, ax = plt.subplots(layout="constrained") pc = ax.pcolormesh(np.random.randn(2, 2)) fig.colorbar(pc) + with pytest.raises(RuntimeError, match='Colorbar layout of new layout'): + fig.set_layout_engine("tight") + fig.set_layout_engine("none") with pytest.raises(RuntimeError, match='Colorbar layout of new layout'): fig.set_layout_engine("tight") @@ -586,6 +590,12 @@ def test_invalid_layouts(): fig.colorbar(pc) with pytest.raises(RuntimeError, match='Colorbar layout of new layout'): fig.set_layout_engine("constrained") + fig.set_layout_engine("none") + with pytest.raises(RuntimeError, match='Colorbar layout of new layout'): + fig.set_layout_engine("constrained") + + fig.set_layout_engine("none") + assert isinstance(fig.get_layout_engine(), PlaceHolderLayoutEngine) @check_figures_equal(extensions=["png", "pdf"]) From 4725daa96810c6ae915ecf4c1dec65025d439eff Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 3 Aug 2022 20:14:20 -0400 Subject: [PATCH 2/2] MNT: changes from review Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/figure.py | 4 ++-- lib/matplotlib/tests/test_figure.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index f5dfdb20062d..441ec703d9cc 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -2414,8 +2414,8 @@ def set_layout_engine(self, layout=None, **kwargs): If `None`, the behavior is controlled by :rc:`figure.autolayout` (which if `True` behaves as if 'tight' were passed) and - :rc:`figure.constrained_layout.use` (which if true behaves as if - 'constrained' were passed). If both are true, + :rc:`figure.constrained_layout.use` (which if `True` behaves as if + 'constrained' were passed). If both are `True`, :rc:`figure.autolayout` takes priority. Users and libraries can define their own layout engines and pass diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index b871d22c2b37..b2da459d6cf9 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -591,12 +591,11 @@ def test_invalid_layouts(): with pytest.raises(RuntimeError, match='Colorbar layout of new layout'): fig.set_layout_engine("constrained") fig.set_layout_engine("none") + assert isinstance(fig.get_layout_engine(), PlaceHolderLayoutEngine) + with pytest.raises(RuntimeError, match='Colorbar layout of new layout'): fig.set_layout_engine("constrained") - fig.set_layout_engine("none") - assert isinstance(fig.get_layout_engine(), PlaceHolderLayoutEngine) - @check_figures_equal(extensions=["png", "pdf"]) def test_add_artist(fig_test, fig_ref): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy