8000 ENH: rect for constrained_layout by jklymak · Pull Request #22627 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

ENH: rect for constrained_layout #22627

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 1 commit into from
May 8, 2022
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
15 changes: 10 additions & 5 deletions lib/matplotlib/_constrained_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()``
Expand All @@ -87,14 +87,18 @@ 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[left, bottom, width, height], each from 0-1.
(left, bottom, width, height), each from 0-1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should one possibly add default values here? (And in other locations.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, doesn't look like there is a renderer parameter (anymore?).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, needed to rebase.


Returns
-------
layoutgrid : private debugging structure
"""

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'
Expand Down Expand Up @@ -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.

Expand All @@ -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()
Expand Down
19 changes: 11 additions & 8 deletions lib/matplotlib/_layoutgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
22 changes: 16 additions & 6 deletions lib/matplotlib/layout_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.

Expand All @@ -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'],
< 6D47 /td> 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):
"""
Expand All @@ -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.

Expand All @@ -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:
Expand Down
18 changes: 18 additions & 0 deletions lib/matplotlib/tests/test_constrainedlayout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 0 additions & 7 deletions pytest.ini

This file was deleted.

34 changes: 0 additions & 34 deletions tests.py
View file Open in desktop

This file was deleted.

0