diff --git a/lib/matplotlib/_constrained_layout.py b/lib/matplotlib/_constrained_layout.py index 891b1ca8b065..db8d8c5fe30b 100644 --- a/lib/matplotlib/_constrained_layout.py +++ b/lib/matplotlib/_constrained_layout.py @@ -64,7 +64,7 @@ ###################################################### def do_constrained_layout(fig, h_pad, w_pad, - hspace=None, wspace=None): + hspace=None, wspace=None, rect=(0, 0, 1, 1)): """ Do the constrained_layout. Called at draw time in ``figure.constrained_layout()`` @@ -87,6 +87,10 @@ def do_constrained_layout(fig, h_pad, w_pad, of 0.1 of the figure width between each column. If h/wspace < h/w_pad, then the pads are used instead. + rect : tuple of 4 floats + Rectangle in figure coordinates to perform constrained layout in + [left, bottom, width, height], each from 0-1. + Returns ------- layoutgrid : private debugging structure @@ -94,7 +98,7 @@ def do_constrained_layout(fig, h_pad, w_pad, renderer = get_renderer(fig) # make layoutgrid tree... - layoutgrids = make_layoutgrids(fig, None) + layoutgrids = make_layoutgrids(fig, None, rect=rect) if not layoutgrids['hasgrids']: _api.warn_external('There are no gridspecs with layoutgrids. ' 'Possibly did not call parent GridSpec with the' @@ -133,7 +137,7 @@ def do_constrained_layout(fig, h_pad, w_pad, return layoutgrids -def make_layoutgrids(fig, layoutgrids): +def make_layoutgrids(fig, layoutgrids, rect=(0, 0, 1, 1)): """ Make the layoutgrid tree. @@ -147,8 +151,9 @@ def make_layoutgrids(fig, layoutgrids): layoutgrids = dict() layoutgrids['hasgrids'] = False if not hasattr(fig, '_parent'): - # top figure - layoutgrids[fig] = mlayoutgrid.LayoutGrid(parent=None, name='figlb') + # top figure; pass rect as parent to allow user-specified + # margins + layoutgrids[fig] = mlayoutgrid.LayoutGrid(parent=rect, name='figlb') else: # subfigure gs = fig._subplotspec.get_gridspec() diff --git a/lib/matplotlib/_layoutgrid.py b/lib/matplotlib/_layoutgrid.py index 8b7b140f600b..90c7b3210e0d 100644 --- a/lib/matplotlib/_layoutgrid.py +++ b/lib/matplotlib/_layoutgrid.py @@ -39,7 +39,7 @@ def __init__(self, parent=None, parent_pos=(0, 0), self.parent_pos = parent_pos self.parent_inner = parent_inner self.name = name + seq_id() - if parent is not None: + if isinstance(parent, LayoutGrid): self.name = f'{parent.name}.{self.name}' self.nrows = nrows self.ncols = ncols @@ -51,8 +51,10 @@ def __init__(self, parent=None, parent_pos=(0, 0), self.width_ratios = np.ones(ncols) sn = self.name + '_' - if parent is None: - self.parent = None + if not isinstance(parent, LayoutGrid): + # parent can be a rect if not a LayoutGrid + # allows specifying a rectangle to contain the layout. + self.parent = parent self.solver = kiwi.Solver() else: self.parent = parent @@ -178,12 +180,13 @@ def parent_constraints(self): # parent's left, the last column right equal to the # parent's right... parent = self.parent - if parent is None: - hc = [self.lefts[0] == 0, - self.rights[-1] == 1, + if not isinstance(parent, LayoutGrid): + # specify a rectangle in figure coordinates + hc = [self.lefts[0] == parent[0], + self.rights[-1] == parent[0] + parent[2], # top and bottom reversed order... - self.tops[0] == 1, - self.bottoms[-1] == 0] + self.tops[0] == parent[1] + parent[3], + self.bottoms[-1] == parent[1]] else: rows, cols = self.parent_pos rows = np.atleast_1d(rows) diff --git a/lib/matplotlib/layout_engine.py b/lib/matplotlib/layout_engine.py index aeaf44923d66..fa4281a2ba02 100644 --- a/lib/matplotlib/layout_engine.py +++ b/lib/matplotlib/layout_engine.py @@ -123,7 +123,7 @@ def __init__(self, *, pad=1.08, h_pad=None, w_pad=None, h_pad, w_pad : float Padding (height/width) between edges of adjacent subplots. Defaults to *pad*. - rect : tuple[float, float, float, float], optional + rect : tuple of 4 floats, optional (left, bottom, right, top) rectangle in normalized figure coordinates that the subplots (including labels) will fit into. Defaults to using the entire figure. @@ -179,7 +179,8 @@ class ConstrainedLayoutEngine(LayoutEngine): _colorbar_gridspec = False def __init__(self, *, h_pad=None, w_pad=None, - hspace=None, wspace=None, **kwargs): + hspace=None, wspace=None, rect=(0, 0, 1, 1), + **kwargs): """ Initialize ``constrained_layout`` settings. @@ -197,15 +198,20 @@ def __init__(self, *, h_pad=None, w_pad=None, If h/wspace < h/w_pad, then the pads are used instead. Default to :rc:`figure.constrained_layout.hspace` and :rc:`figure.constrained_layout.wspace`. + rect : tuple of 4 floats + Rectangle in figure coordinates to perform constrained layout in + (left, bottom, width, height), each from 0-1. """ 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'], wspace=mpl.rcParams['figure.constrained_layout.wspace'], - hspace=mpl.rcParams['figure.constrained_layout.hspace']) + hspace=mpl.rcParams['figure.constrained_layout.hspace'], + rect=(0, 0, 1, 1)) # 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.set(w_pad=w_pad, h_pad=h_pad, wspace=wspace, hspace=hspace, + rect=rect) def execute(self, fig): """ @@ -222,10 +228,11 @@ def execute(self, fig): return do_constrained_layout(fig, w_pad=w_pad, h_pad=h_pad, wspace=self._params['wspace'], - hspace=self._params['hspace']) + hspace=self._params['hspace'], + rect=self._params['rect']) def set(self, *, h_pad=None, w_pad=None, - hspace=None, wspace=None): + hspace=None, wspace=None, rect=None): """ Set the pads for constrained_layout. @@ -243,6 +250,9 @@ def set(self, *, h_pad=None, w_pad=None, If h/wspace < h/w_pad, then the pads are used instead. Default to :rc:`figure.constrained_layout.hspace` and :rc:`figure.constrained_layout.wspace`. + rect : tuple of 4 floats + Rectangle in figure coordinates to perform constrained layout in + (left, bottom, width, height), each from 0-1. """ for td in self.set.__kwdefaults__: if locals()[td] is not None: diff --git a/lib/matplotlib/tests/test_constrainedlayout.py b/lib/matplotlib/tests/test_constrainedlayout.py index b3aea15adad3..bd84d744ac11 100644 --- a/lib/matplotlib/tests/test_constrainedlayout.py +++ b/lib/matplotlib/tests/test_constrainedlayout.py @@ -608,3 +608,21 @@ def test_discouraged_api(): def test_kwargs(): fig, ax = plt.subplots(constrained_layout={'h_pad': 0.02}) fig.draw_without_rendering() + + +def test_rect(): + fig, ax = plt.subplots(layout='constrained') + fig.get_layout_engine().set(rect=[0, 0, 0.5, 0.5]) + fig.draw_without_rendering() + ppos = ax.get_position() + assert ppos.x1 < 0.5 + assert ppos.y1 < 0.5 + + fig, ax = plt.subplots(layout='constrained') + fig.get_layout_engine().set(rect=[0.2, 0.2, 0.3, 0.3]) + fig.draw_without_rendering() + ppos = ax.get_position() + assert ppos.x1 < 0.5 + assert ppos.y1 < 0.5 + assert ppos.x0 > 0.2 + assert ppos.y0 > 0.2 diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 905d3a5e5a52..000000000000 --- a/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -# Additional configuration is in matplotlib/testing/conftest.py. -[pytest] -minversion = 3.6 - -testpaths = lib -python_files = test_*.py -junit_family = xunit2 diff --git a/tests.py b/tests.py deleted file mode 100755 index 335fa860fcec..000000000000 --- a/tests.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -# This allows running the matplotlib tests from the command line: e.g. -# -# $ python tests.py -v -d -# -# The arguments are identical to the arguments accepted by pytest. -# -# See http://doc.pytest.org/ for a detailed description of these options. - -import sys -import argparse - - -if __name__ == '__main__': - try: - from matplotlib import test - except ImportError: - print('matplotlib.test could not be imported.\n\n' - 'Try a virtual env and `pip install -e .`') - sys.exit(-1) - - parser = argparse.ArgumentParser(add_help=False) - parser.add_argument('--recursionlimit', type=int, default=None, - help='Specify recursionlimit for test run') - args, extra_args = parser.parse_known_args() - - print('Python byte-compilation optimization level:', sys.flags.optimize) - - if args.recursionlimit is not None: # Will trigger deprecation. - retcode = test(argv=extra_args, recursionlimit=args.recursionlimit) - else: - retcode = test(argv=extra_args) - sys.exit(retcode) 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