Skip to content

Add Figure parameter layout and discourage tight_layout / constrained_layout #19892

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/api/next_api_changes/deprecations/19892-TH.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Discouraged: ``Figure`` parameters *tight_layout* and *constrained_layout*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``Figure`` parameters *tight_layout* and *constrained_layout* are
triggering competing layout mechanisms and thus should not be used together.

To make the API clearer, we've merged them under the new parameter *layout*
with values 'constrained' (equal to ``constrained_layout=True``), 'tight'
(equal to ``tight_layout=True``). If given *layout* takes precedence.

The use of *tight_layout* and *constrained_layout* is discouraged in favor
of *layout*. However, these parameters will stay available for backward
compatibility.
67 changes: 55 additions & 12 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,8 @@ def __init__(self,
subplotpars=None, # rc figure.subplot.*
tight_layout=None, # rc figure.autolayout
constrained_layout=None, # rc figure.constrained_layout.use
*,
layout=None,
**kwargs
):
"""
Expand Down Expand Up @@ -2178,19 +2180,42 @@ def __init__(self,
parameters :rc:`figure.subplot.*` are used.

tight_layout : bool or dict, default: :rc:`figure.autolayout`
If ``False`` use *subplotpars*. If ``True`` adjust subplot
parameters using `.tight_layout` with default padding.
When providing a dict containing the keys ``pad``, ``w_pad``,
``h_pad``, and ``rect``, the default `.tight_layout` paddings
will be overridden.
Whether to use the tight layout mechanism. See `.set_tight_layout`.

.. admonition:: Discouraged

The use of this parameter is discouraged. Please use
``layout='tight'`` instead for the common case of
``tight_layout=True`` and use `.set_tight_layout` otherwise.

constrained_layout : bool, default: :rc:`figure.constrained_layout.use`
If ``True`` use constrained layout to adjust positioning of plot
elements. Like ``tight_layout``, but designed to be more
flexible. See
:doc:`/tutorials/intermediate/constrainedlayout_guide`
for examples. (Note: does not work with `add_subplot` or
`~.pyplot.subplot2grid`.)
This is equal to ``layout='constrained'``.

.. admonition:: Discouraged

The use of this parameter is discouraged. Please use
``layout='constrained'`` instead.

layout : {'constrained', 'tight'}, optional
The layout mechanism for positioning of plot elements.
Supported values:

- 'constrained': The constrained layout solver usually gives the
best layout results and is thus recommended. However, it is
computationally expensive and can be slow for complex figures
with many elements.

See :doc:`/tutorials/intermediate/constrainedlayout_guide`
for examples.

- 'tight': Use the tight layout mechanism. This is a relatively
simple algorithm, that adjusts the subplot parameters so that
decorations like tick labels, axis labels and titles have enough
space. See `.Figure.set_tight_layout` for further details.

If not given, fall back to using the parameters *tight_layout* and
*constrained_layout*, including their config defaults
:rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`.

Other Parameters
----------------
Expand All @@ -2200,6 +2225,24 @@ def __init__(self,
"""
super().__init__(**kwargs)

if layout is not None:
if tight_layout is not None:
_api.warn_external(
"The Figure parameters 'layout' and 'tight_layout' "
"cannot be used together. Please use 'layout' only.")
if constrained_layout is not None:
_api.warn_external(
"The Figure parameters 'layout' and 'constrained_layout' "
"cannot be used together. Please use 'layout' only.")
if layout == 'constrained':
tight_layout = False
constrained_layout = True
elif layout == 'tight':
tight_layout = True
constrained_layout = False
else:
_api.check_in_list(['constrained', 'tight'], layout=layout)

self.callbacks = cbook.CallbackRegistry()
# Callbacks traditionally associated with the canvas (and exposed with
# a proxy property), but that actually need to be on the figure for
Expand Down Expand Up @@ -2362,7 +2405,7 @@ def set_tight_layout(self, tight):
----------
tight : bool or dict with keys "pad", "w_pad", "h_pad", "rect" or None
If a bool, sets whether to call `.tight_layout` upon drawing.
If ``None``, use the ``figure.autolayout`` rcparam instead.
If ``None``, use :rc:`figure.autolayout` instead.
If a dict, pass it as kwargs to `.tight_layout`, overriding the
default paddings.
"""
Expand Down
33 changes: 32 additions & 1 deletion lib/matplotlib/tests/test_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,13 +501,44 @@ def test_figure_repr():
assert repr(fig) == "<Figure size 100x200 with 0 Axes>"


def test_warn_cl_plus_tl():
def test_valid_layouts():
fig = Figure(layout=None)
assert not fig.get_tight_layout()
assert not fig.get_constrained_layout()

fig = Figure(layout='tight')
assert fig.get_tight_layout()
assert not fig.get_constrained_layout()

fig = Figure(layout='constrained')
assert not fig.get_tight_layout()
assert fig.get_constrained_layout()


def test_invalid_layouts():
fig, ax = plt.subplots(constrained_layout=True)
with pytest.warns(UserWarning):
# this should warn,
fig.subplots_adjust(top=0.8)
assert not(fig.get_constrained_layout())

# Using layout + (tight|constrained)_layout warns, but the former takes
# precedence.
with pytest.warns(UserWarning, match="Figure parameters 'layout' and "
"'tight_layout' cannot"):
fig = Figure(layout='tight', tight_layout=False)
assert fig.get_tight_layout()
assert not fig.get_constrained_layout()
with pytest.warns(UserWarning, match="Figure parameters 'layout' and "
"'constrained_layout' cannot"):
fig = Figure(layout='constrained', constrained_layout=False)
assert not fig.get_tight_layout()
assert fig.get_constrained_layout()

with pytest.raises(ValueError,
match="'foobar' is not a valid value for layout"):
Figure(layout='foobar')


@check_figures_equal(extensions=["png", "pdf"])
def test_add_artist(fig_test, fig_ref):
Expand Down