Skip to content

Commit 6cf9c20

Browse files
committed
DOC: add some docs
1 parent 544d829 commit 6cf9c20

File tree

3 files changed

+118
-13
lines changed

3 files changed

+118
-13
lines changed

lib/matplotlib/figure.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,7 +2203,7 @@ def __init__(self,
22032203
The use of this parameter is discouraged. Please use
22042204
``layout='constrained'`` instead.
22052205
2206-
layout : {'constrained', 'tight'}, optional
2206+
layout : {'constrained', 'tight', `.LayoutEngine`}, optional
22072207
The layout mechanism for positioning of plot elements.
22082208
Supported values:
22092209
@@ -2220,6 +2220,10 @@ def __init__(self,
22202220
decorations like tick labels, axis labels and titles have enough
22212221
space. See `.Figure.set_tight_layout` for further details.
22222222
2223+
- A `.LayoutEngine` instance. Native layout instances are
2224+
`.tight_layout_engine` and `.constrained_layout_engine`.
2225+
However other subclasses are possible.
2226+
22232227
If not given, fall back to using the parameters *tight_layout* and
22242228
*constrained_layout*, including their config defaults
22252229
:rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`.
@@ -2232,16 +2236,14 @@ def __init__(self,
22322236
"""
22332237
super().__init__(**kwargs)
22342238
self.layout_engine = None
2239+
22352240
if layout is not None:
2236-
if tight_layout is not None:
2241+
if (tight_layout is not None or
2242+
constrained_layout is not None):
22372243
_api.warn_external(
2238-
"The Figure parameters 'layout' and 'tight_layout' "
2239-
"cannot be used together. Please use 'layout' only.")
2240-
elif constrained_layout is not None:
2241-
_api.warn_external(
2242-
"The Figure parameters 'layout' and 'constrained_layout' "
2243-
"cannot be used together. Please use 'layout' only.")
2244-
_api.check_in_list(['constrained', 'tight'], layout=layout)
2244+
"The Figure parameters 'layout' and 'tight_layout' or "
2245+
"'constrained_layout cannot be used together. Please use "
2246+
"'layout' only.")
22452247
self.set_layout_engine(layout=layout)
22462248
elif tight_layout is not None:
22472249
if constrained_layout is not None:
@@ -2256,6 +2258,9 @@ def __init__(self,
22562258
self.set_layout_engine(layout='constrained')
22572259
if isinstance(constrained_layout, dict):
22582260
self.get_layout_engine().set_info(constrained_layout)
2261+
else:
2262+
# everything is None, so use default:
2263+
self.set_layout_engine(layout=layout)
22592264

22602265
self.callbacks = cbook.CallbackRegistry()
22612266
# Callbacks traditionally associated with the canvas (and exposed with
@@ -2315,7 +2320,17 @@ def __init__(self,
23152320

23162321
def set_layout_engine(self, layout=None, **kwargs):
23172322
"""
2318-
Set the layout engine... FIXME
2323+
Set the layout engine for this figure.
2324+
2325+
Parameters
2326+
----------
2327+
layout: {'constrained', 'tight'} or `~.LayoutEngine`
2328+
'constrained' will use `~.constrained_layout_engine`, 'tight' will
2329+
use `~.tight_layout_engine`. Users and libraries can define their
2330+
own layout engines as well.
2331+
kwargs: dict
2332+
The keyword arguments are passed to the layout engine to set things
2333+
like padding and margin sizes.
23192334
"""
23202335
if layout is None:
23212336
if mpl.rcParams['figure.autolayout']:

lib/matplotlib/layout_engine.py

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
"""
2+
A module to define layout engines for Matplotlib.
3+
4+
Figures have a ``layout_engine`` property that holds a subclass of
5+
`~.LayoutEngine` defined here (or *None* for no layout). At draw time
6+
``figure.layout_engine.execute()`` is called, the goal of which is usually
7+
to rearrange axes on the figure to produce a pleasing layout. Note that
8+
this could also be implimented as a ``draw`` callback, however when
9+
printing we often disable the layout engine for the final draw, so it is
10+
helpful to have a different callback mechanism. It is also useful to
11+
know the layout engine while the figure is being created, in particular
12+
to deal with colorbars, so having a dedicated property of the figure to
13+
querry is useful.
14+
15+
Matplotlib has two native layout engines, ``tight_layout`` and
16+
``constrained_layout``, which are implimented using this formalism
17+
as `.tight_layout_engine` and `.constrained_layout_engine`. While layout
18+
engines tend to be complicated, users and downstream libraries can now create
19+
their own layout engine and connect it to a figure.
20+
"""
121

222
import matplotlib as mpl
323
import matplotlib._api as _api
@@ -11,8 +31,17 @@
1131

1232
class LayoutEngine():
1333
"""
14-
Base class for matplotlib layout engines containing info
15-
we expect all layout engines to have.
34+
Base class for matplotlib layout engines.
35+
36+
A layout engine can be passed to a figure at instantiation or
37+
at any time with `~.figure.Figure.set_layout_engine`. However, note
38+
note that layout engines affect the creation of colorbars, so
39+
`~.figure.Figure.set_layout_engine` should be called before any
40+
colorbars are created.
41+
42+
Once attached to a figure, the layout engine ``execute`` function
43+
is called at draw time by `~.figure.Figure.draw`, providing a special
44+
draw-time hook.
1645
"""
1746
def __init__(self, fig, **kwargs):
1847
self.colorbar_gridspec = True
@@ -34,9 +63,28 @@ def execute(self):
3463

3564
class tight_layout_engine(LayoutEngine):
3665
"""
66+
Impliments the ``tight_layout`` geometry management.
3767
"""
3868

3969
def __init__(self, fig, **kwargs):
70+
"""
71+
Initialize tight_layout engine.
72+
73+
Parameters
74+
----------
75+
fig : `~.figure.Figure`
76+
Figure the layout engine will be used on.
77+
pad : float
78+
Padding between the figure edge and the edges of subplots, as a
79+
fraction of the font size.
80+
h_pad, w_pad : float
81+
Padding (height/width) between edges of adjacent subplots.
82+
Defaults to *pad*.
83+
rect : tuple[float, float, float, float], optional
84+
(left, bottom, right, top) rectangle in normalized figure
85+
coordinates that the whole subplots area (including labels)
86+
will fit into. Defaults to using the entire figure.
87+
"""
4088
self.colorbar_gridspec = True
4189
self.figure = fig
4290
self._keys = ['pad', 'h_pad', 'w_pad', 'rect']
@@ -47,6 +95,15 @@ def __init__(self, fig, **kwargs):
4795
self.adjust_compatible = True
4896

4997
def execute(self):
98+
"""
99+
Execute tight_layout.
100+
101+
This decides the subplot parameters given the padding that
102+
will allow the axes labels to not be covered by other labels
103+
and axes.
104+
105+
See also: `.figure.Figure.tight_layout` and `.pyplot.tight_layout`.
106+
"""
50107
fig = self.figure
51108
info = self._info
52109
subplotspec_list = get_subplotspec_list(fig.axes)
@@ -83,6 +140,22 @@ class constrained_layout_engine(LayoutEngine):
83140
"""
84141

85142
def __init__(self, fig, **kwargs):
143+
"""
144+
Initialize ``constrained_layout`` settings.
145+
146+
Parameters
147+
----------
148+
fig : `~.figure.Figure`
149+
Figure the layout engine will be used on.
150+
h_pad, w_pad : float
151+
Padding around the axes elements in figure-normalized units.
152+
hspace, wspace : float
153+
Fraction of the figure to dedicate to space between the
154+
axes. These are evenly spread between the gaps between the axes.
155+
A value of 0.2 for a three-column layout would have a space
156+
of 0.1 of the figure width between each column.
157+
If h/wspace < h/w_pad, then the pads are used instead.
158+
"""
86159
self.colorbar_gridspec = False
87160
self.figure = fig
88161
todo = ['w_pad', 'h_pad', 'wspace', 'hspace']
@@ -93,6 +166,9 @@ def __init__(self, fig, **kwargs):
93166
self.adjust_compatible = False
94167

95168
def execute(self):
169+
"""
170+
Perform constrained_layout and move and resize axes accordingly.
171+
"""
96172
width, height = self.figure.get_size_inches()
97173
# pads are relative to the current state of the figure...
98174
w_pad = self._info['w_pad'] / width
@@ -103,6 +179,20 @@ def execute(self):
103179
hspace=self._info['hspace'])
104180

105181
def set_info(self, **kwargs):
182+
"""
183+
Set the pads for constrained_layout
184+
185+
Parameters
186+
----------
187+
h_pad, w_pad : float
188+
Padding around the axes elements in figure-normalized units.
189+
hspace, wspace : float
190+
Fraction of the figure to dedicate to space between the
191+
axes. These are evenly spread between the gaps between the axes.
192+
A value of 0.2 for a three-column layout would have a space
193+
of 0.1 of the figure width between each column.
194+
If h/wspace < h/w_pad, then the pads are used instead.
195+
"""
106196
todo = ['w_pad', 'h_pad', 'wspace', 'hspace']
107197
for k in kwargs.keys():
108198
if k not in todo:

tutorials/intermediate/constrainedlayout_guide.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ def example_plot(ax, fontsize=12, hide_labels=False):
237237
#
238238
# Padding between axes is controlled in the horizontal by *w_pad* and
239239
# *wspace*, and vertical by *h_pad* and *hspace*. These can be edited
240-
# via `~.figure.Figure.get_layout_engine().set_info`. *w/h_pad* are
240+
# via ``fig.get_layout_engine().set_info``. *w/h_pad* are
241241
# the minimum space around the axes in units of inches:
242242

243243
fig, axs = plt.subplots(2, 2, layout="constrained")

0 commit comments

Comments
 (0)