diff --git a/lib/matplotlib/layout_engine.py b/lib/matplotlib/layout_engine.py index 8ec29f741154..2e4997248320 100644 --- a/lib/matplotlib/layout_engine.py +++ b/lib/matplotlib/layout_engine.py @@ -30,29 +30,39 @@ class LayoutEngine: Base class for Matplotlib layout engines. A layout engine can be passed to a figure at instantiation or at any time - with `~.figure.Figure.set_layout_engine`. However, note note that layout - engines affect the creation of colorbars, so - `~.figure.Figure.set_layout_engine` should be called before any colorbars - are created. - - Once attached to a figure, the layout engine ``execute`` function is called - at draw time by `~.figure.Figure.draw`, providing a special draw-time hook. - - Currently, there are two properties of ``LayoutEngine`` classes that are - consulted while manipulating the figure. ``engine.colorbar_gridspec`` - tells `.Figure.colorbar` whether to make the axes using the gridspec - method (see `.colorbar.make_axes_gridspec`) or not - (see `.colorbar.make_axes`); `.ConstrainedLayoutEngine` sets this to - *False*, `.TightLayoutEngine` to *True*. The second property is - ``engine.adjust_compatible`` that stops `.Figure.subplots_adjust` from - being run if it is not compatible with the layout engine - (`.ConstrainedLayoutEngine` sets this to *False* also). + with `~.figure.Figure.set_layout_engine`. Once attached to a figure, the + layout engine ``execute`` function is called at draw time by + `~.figure.Figure.draw`, providing a special draw-time hook. + + .. note :: + + However, note that layout engines affect the creation of colorbars, so + `~.figure.Figure.set_layout_engine` should be called before any + colorbars are created. + + Currently, there are two properties of `LayoutEngine` classes that are + consulted while manipulating the figure: + + - ``engine.colorbar_gridspec`` tells `.Figure.colorbar` whether to make the + axes using the gridspec method (see `.colorbar.make_axes_gridspec`) or + not (see `.colorbar.make_axes`); + - ``engine.adjust_compatible`` stops `.Figure.subplots_adjust` from being run + if it is not compatible with the layout engine. + + To implement a custom `LayoutEngine`: + + 1. override ``_adjust_compatible`` and ``_colorbar_gridspec`` + 2. override `LayoutEngine.set` to update *self._params* + 3. override `LayoutEngine.execute` with your implementation + """ + # override these is sub-class + _adjust_compatible = None + _colorbar_gridspec = None - def __init__(self): + def __init__(self, **kwargs): + super().__init__(**kwargs) self._params = {} - self._adjust_compatible = True - self._colorbar_gridspec = True def set(self, **kwargs): raise NotImplementedError @@ -63,6 +73,8 @@ def colorbar_gridspec(self): Return a boolean if the layout engine creates colorbars using a gridspec. """ + if self._colorbar_gridspec is None: + raise NotImplementedError return self._colorbar_gridspec @property @@ -71,13 +83,15 @@ def adjust_compatible(self): Return a boolean if the layout engine is compatible with `~.Figure.subplots_adjust`. """ + if self._adjust_compatible is None: + raise NotImplementedError return self._adjust_compatible def get(self): """ - Return the parameters for the layout engine. + Return copy of the parameters for the layout engine. """ - return self._params + return dict(self._params) def execute(self, fig): """ @@ -92,9 +106,11 @@ class TightLayoutEngine(LayoutEngine): Implements the ``tight_layout`` geometry management. See :doc:`/tutorials/intermediate/tight_layout_guide` for details. """ + _adjust_compatible = True + _colorbar_gridspec = True def __init__(self, *, pad=1.08, h_pad=None, w_pad=None, - rect=(0, 0, 1, 1)): + rect=(0, 0, 1, 1), **kwargs): """ Initialize tight_layout engine. @@ -111,13 +127,11 @@ def __init__(self, *, pad=1.08, h_pad=None, w_pad=None, coordinates that the subplots (including labels) will fit into. Defaults to using the entire figure. """ - super().__init__() + super().__init__(**kwargs) for td in ['pad', 'h_pad', 'w_pad', 'rect']: # initialize these in case None is passed in above: self._params[td] = None self.set(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) - self._adjust_compatible = True - self._colorbar_gridspec = True def execute(self, fig): """ @@ -160,8 +174,11 @@ class ConstrainedLayoutEngine(LayoutEngine): :doc:`/tutorials/intermediate/constrained_layout_guide` for details. """ + _adjust_compatible = False + _colorbar_gridspec = False + def __init__(self, *, h_pad=None, w_pad=None, - hspace=None, wspace=None): + hspace=None, wspace=None, **kwargs): """ Initialize ``constrained_layout`` settings. @@ -180,7 +197,7 @@ def __init__(self, *, h_pad=None, w_pad=None, Default to :rc:`figure.constrained_layout.hspace` and :rc:`figure.constrained_layout.wspace`. """ - super().__init__() + super().__init__(**kwargs) # set the defaults: self.set(w_pad=mpl.rcParams['figure.constrained_layout.w_pad'], h_pad=mpl.rcParams['figure.constrained_layout.h_pad'], @@ -188,8 +205,6 @@ def __init__(self, *, h_pad=None, w_pad=None, hspace=mpl.rcParams['figure.constrained_layout.hspace']) # set anything that was passed in (None will be ignored): self.set(w_pad=w_pad, h_pad=h_pad, wspace=wspace, hspace=hspace) - self._adjust_compatible = False - self._colorbar_gridspec = False def execute(self, fig): """ diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index c7e9e2277c26..80e210c380cd 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -585,10 +585,11 @@ def test_invalid_layouts(): # Using layout + (tight|constrained)_layout warns, but the former takes # precedence. - wst = "The Figure parameters 'layout' and 'tight_layout' or " + wst = "The Figure parameters 'layout' and 'tight_layout'" with pytest.warns(UserWarning, match=wst): fig = Figure(layout='tight', tight_layout=False) assert isinstance(fig.get_layout_engine(), TightLayoutEngine) + wst = "The Figure parameters 'layout' and 'constrained_layout'" with pytest.warns(UserWarning, match=wst): fig = Figure(layout='constrained', constrained_layout=False) assert not isinstance(fig.get_layout_engine(), TightLayoutEngine)