Skip to content

Commit acae2c4

Browse files
committed
ENH: add rect parameter to constrained_layout
1 parent 698397f commit acae2c4

File tree

4 files changed

+54
-18
lines changed

4 files changed

+54
-18
lines changed

lib/matplotlib/_constrained_layout.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
######################################################
6666
def do_constrained_layout(fig, h_pad, w_pad,
67-
hspace=None, wspace=None):
67+
hspace=None, wspace=None, rect=(0, 0, 1, 1)):
6868
"""
6969
Do the constrained_layout. Called at draw time in
7070
``figure.constrained_layout()``
@@ -87,14 +87,18 @@ def do_constrained_layout(fig, h_pad, w_pad,
8787
of 0.1 of the figure width between each column.
8888
If h/wspace < h/w_pad, then the pads are used instead.
8989
90+
rect : tuple of 4 floats
91+
Rectangle in figure coordinates to perform constrained layout in
92+
[left, bottom, width, height], each from 0-1.
93+
9094
Returns
9195
-------
9296
layoutgrid : private debugging structure
9397
"""
9498

9599
renderer = _get_renderer(fig)
96100
# make layoutgrid tree...
97-
layoutgrids = make_layoutgrids(fig, None)
101+
layoutgrids = make_layoutgrids(fig, None, rect=rect)
98102
if not layoutgrids['hasgrids']:
99103
_api.warn_external('There are no gridspecs with layoutgrids. '
100104
'Possibly did not call parent GridSpec with the'
@@ -133,7 +137,7 @@ def do_constrained_layout(fig, h_pad, w_pad,
133137
return layoutgrids
134138

135139

136-
def make_layoutgrids(fig, layoutgrids):
140+
def make_layoutgrids(fig, layoutgrids, rect=[0, 0, 1, 1]):
137141
"""
138142
Make the layoutgrid tree.
139143
@@ -147,8 +151,9 @@ def make_layoutgrids(fig, layoutgrids):
147151
layoutgrids = dict()
148152
layoutgrids['hasgrids'] = False
149153
if not hasattr(fig, '_parent'):
150-
# top figure
151-
layoutgrids[fig] = mlayoutgrid.LayoutGrid(parent=None, name='figlb')
154+
# top figure; pass rect as parent to allow user-specified
155+
# margins
156+
layoutgrids[fig] = mlayoutgrid.LayoutGrid(parent=rect, name='figlb')
152157
else:
153158
# subfigure
154159
gs = fig._subplotspec.get_gridspec()

lib/matplotlib/_layoutgrid.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def __init__(self, parent=None, parent_pos=(0, 0),
3939
self.parent_pos = parent_pos
4040
self.parent_inner = parent_inner
4141
self.name = name + seq_id()
42-
if parent is not None:
42+
if isinstance(parent, LayoutGrid):
4343
self.name = f'{parent.name}.{self.name}'
4444
self.nrows = nrows
4545
self.ncols = ncols
@@ -51,8 +51,10 @@ def __init__(self, parent=None, parent_pos=(0, 0),
5151
self.width_ratios = np.ones(ncols)
5252

5353
sn = self.name + '_'
54-
if parent is None:
55-
self.parent = None
54+
if not isinstance(parent, LayoutGrid):
55+
# parent can be a rect if not a LayoutGrid
56+
# allows specifying a rectangle to contain the layout.
57+
self.parent = parent
5658
self.solver = kiwi.Solver()
5759
else:
5860
self.parent = parent
@@ -178,12 +180,13 @@ def parent_constraints(self):
178180
# parent's left, the last column right equal to the
179181
# parent's right...
180182
parent = self.parent
181-
if parent is None:
182-
hc = [self.lefts[0] == 0,
183-
self.rights[-1] == 1,
183+
if not isinstance(parent, LayoutGrid):
184+
# specify a rectangle in figure coordinates
185+
hc = [self.lefts[0] == parent[0],
186+
self.rights[-1] == parent[0] + parent[2],
184187
# top and bottom reversed order...
185-
self.tops[0] == 1,
186-
self.bottoms[-1] == 0]
188+
self.tops[0] == parent[1] + parent[3],
189+
self.bottoms[-1] == parent[1]]
187190
else:
188191
rows, cols = self.parent_pos
189192
rows = np.atleast_1d(rows)

lib/matplotlib/layout_engine.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ class ConstrainedLayoutEngine(LayoutEngine):
178178
_colorbar_gridspec = False
179179

180180
def __init__(self, *, h_pad=None, w_pad=None,
181-
hspace=None, wspace=None, **kwargs):
181+
hspace=None, wspace=None, rect=[0, 0, 1, 1],
182+
**kwargs):
182183
"""
183184
Initialize ``constrained_layout`` settings.
184185
@@ -196,15 +197,20 @@ def __init__(self, *, h_pad=None, w_pad=None,
196197
If h/wspace < h/w_pad, then the pads are used instead.
197198
Default to :rc:`figure.constrained_layout.hspace` and
198199
:rc:`figure.constrained_layout.wspace`.
200+
rect : [l, b, w, h]
201+
Rectangle in figure coordinates to perform constrained layout in
202+
[left, bottom, width, height], each from 0-1.
199203
"""
200204
super().__init__(**kwargs)
201205
# set the defaults:
202206
self.set(w_pad=mpl.rcParams['figure.constrained_layout.w_pad'],
203207
h_pad=mpl.rcParams['figure.constrained_layout.h_pad'],
204208
wspace=mpl.rcParams['figure.constrained_layout.wspace'],
205-
hspace=mpl.rcParams['figure.constrained_layout.hspace'])
209+
hspace=mpl.rcParams['figure.constrained_layout.hspace'],
210+
rect=[0, 0, 1, 1])
206211
# set anything that was passed in (None will be ignored):
207-
self.set(w_pad=w_pad, h_pad=h_pad, wspace=wspace, hspace=hspace)
212+
self.set(w_pad=w_pad, h_pad=h_pad, wspace=wspace, hspace=hspace,
213+
rect=rect)
208214

209215
def execute(self, fig):
210216
"""
@@ -221,10 +227,11 @@ def execute(self, fig):
221227

222228
return do_constrained_layout(fig, w_pad=w_pad, h_pad=h_pad,
223229
wspace=self._params['wspace'],
224-
hspace=self._params['hspace'])
230+
hspace=self._params['hspace'],
231+
rect=self._params['rect'])
225232

226233
def set(self, *, h_pad=None, w_pad=None,
227-
hspace=None, wspace=None):
234+
hspace=None, wspace=None, rect=None):
228235
"""
229236
Set the pads for constrained_layout.
230237
@@ -242,6 +249,9 @@ def set(self, *, h_pad=None, w_pad=None,
242249
If h/wspace < h/w_pad, then the pads are used instead.
243250
Default to :rc:`figure.constrained_layout.hspace` and
244251
:rc:`figure.constrained_layout.wspace`.
252+
rect : [l, b, w, h]
253+
Rectangle in figure coordinates to perform constrained layout in
254+
[left, bottom, width, height], each from 0-1.
245255
"""
246256
for td in self.set.__kwdefaults__:
247257
if locals()[td] is not None:

lib/matplotlib/tests/test_constrainedlayout.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,21 @@ def test_discouraged_api():
608608
def test_kwargs():
609609
fig, ax = plt.subplots(constrained_layout={'h_pad': 0.02})
610610
fig.draw_without_rendering()
611+
612+
613+
def test_rect():
614+
fig, ax = plt.subplots(layout='constrained')
615+
fig.get_layout_engine().set(rect=[0, 0, 0.5, 0.5])
616+
fig.draw_without_rendering()
617+
ppos = ax.get_position()
618+
assert ppos.x1 < 0.5
619+
assert ppos.y1 < 0.5
620+
621+
fig, ax = plt.subplots(layout='constrained')
622+
fig.get_layout_engine().set(rect=[0.2, 0.2, 0.3, 0.3])
623+
fig.draw_without_rendering()
624+
ppos = ax.get_position()
625+
assert ppos.x1 < 0.5
626+
assert ppos.y1 < 0.5
627+
assert ppos.x0 > 0.2
628+
assert ppos.y0 > 0.2

0 commit comments

Comments
 (0)
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