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
+ """
1
21
2
22
import matplotlib as mpl
3
23
import matplotlib ._api as _api
11
31
12
32
class LayoutEngine ():
13
33
"""
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.
16
45
"""
17
46
def __init__ (self , fig , ** kwargs ):
18
47
self .colorbar_gridspec = True
@@ -34,9 +63,28 @@ def execute(self):
34
63
35
64
class tight_layout_engine (LayoutEngine ):
36
65
"""
66
+ Impliments the ``tight_layout`` geometry management.
37
67
"""
38
68
39
69
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
+ """
40
88
self .colorbar_gridspec = True
41
89
self .figure = fig
42
90
self ._keys = ['pad' , 'h_pad' , 'w_pad' , 'rect' ]
@@ -47,6 +95,15 @@ def __init__(self, fig, **kwargs):
47
95
self .adjust_compatible = True
48
96
49
97
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
+ """
50
107
fig = self .figure
51
108
info = self ._info
52
109
subplotspec_list = get_subplotspec_list (fig .axes )
@@ -83,6 +140,22 @@ class constrained_layout_engine(LayoutEngine):
83
140
"""
84
141
85
142
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
+ """
86
159
self .colorbar_gridspec = False
87
160
self .figure = fig
88
161
todo = ['w_pad' , 'h_pad' , 'wspace' , 'hspace' ]
@@ -93,6 +166,9 @@ def __init__(self, fig, **kwargs):
93
166
self .adjust_compatible = False
94
167
95
168
def execute (self ):
169
+ """
170
+ Perform constrained_layout and move and resize axes accordingly.
171
+ """
96
172
width , height = self .figure .get_size_inches ()
97
173
# pads are relative to the current state of the figure...
98
174
w_pad = self ._info ['w_pad' ] / width
@@ -103,6 +179,20 @@ def execute(self):
103
179
hspace = self ._info ['hspace' ])
104
180
105
181
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
+ """
106
196
todo = ['w_pad' , 'h_pad' , 'wspace' , 'hspace' ]
107
197
for k in kwargs .keys ():
108
198
if k not in todo :
0 commit comments