Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/matplotlib/tight_layout.py : 6%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2This module provides routines to adjust subplot params so that subplots are
3nicely fit in the figure. In doing so, only axis labels, tick labels, axes
4titles and offsetboxes that are anchored to axes are currently considered.
6Internally, it assumes that the margins (left_margin, etc.) which are
7differences between ax.get_tightbbox and ax.bbox are independent of axes
8position. This may fail if Axes.adjustable is datalim. Also, This will fail
9for some cases (for example, left or right margin is affected by xlabel).
10"""
12from matplotlib import cbook, rcParams
13from matplotlib.font_manager import FontProperties
14from matplotlib.transforms import TransformedBbox, Bbox
17def _get_left(tight_bbox, axes_bbox):
18 return axes_bbox.xmin - tight_bbox.xmin
21def _get_right(tight_bbox, axes_bbox):
22 return tight_bbox.xmax - axes_bbox.xmax
25def _get_bottom(tight_bbox, axes_bbox):
26 return axes_bbox.ymin - tight_bbox.ymin
29def _get_top(tight_bbox, axes_bbox):
30 return tight_bbox.ymax - axes_bbox.ymax
33def auto_adjust_subplotpars(
34 fig, renderer, nrows_ncols, num1num2_list, subplot_list,
35 ax_bbox_list=None, pad=1.08, h_pad=None, w_pad=None, rect=None):
36 """
37 Return a dict of subplot parameters to adjust spacing between subplots
38 or ``None`` if resulting axes would have zero height or width.
40 Note that this function ignores geometry information of subplot
41 itself, but uses what is given by the *nrows_ncols* and *num1num2_list*
42 parameters. Also, the results could be incorrect if some subplots have
43 ``adjustable=datalim``.
45 Parameters
46 ----------
47 nrows_ncols : Tuple[int, int]
48 Number of rows and number of columns of the grid.
49 num1num2_list : List[int]
50 List of numbers specifying the area occupied by the subplot
51 subplot_list : list of subplots
52 List of subplots that will be used to calculate optimal subplot_params.
53 pad : float
54 Padding between the figure edge and the edges of subplots, as a
55 fraction of the font size.
56 h_pad, w_pad : float
57 Padding (height/width) between edges of adjacent subplots, as a
58 fraction of the font size. Defaults to *pad*.
59 rect : Tuple[float, float, float, float]
60 [left, bottom, right, top] in normalized (0, 1) figure coordinates.
61 """
62 rows, cols = nrows_ncols
64 font_size_inches = (
65 FontProperties(size=rcParams["font.size"]).get_size_in_points() / 72)
66 pad_inches = pad * font_size_inches
67 if h_pad is not None:
68 vpad_inches = h_pad * font_size_inches
69 else:
70 vpad_inches = pad_inches
72 if w_pad is not None:
73 hpad_inches = w_pad * font_size_inches
74 else:
75 hpad_inches = pad_inches
77 if len(num1num2_list) != len(subplot_list) or len(subplot_list) == 0:
78 raise ValueError
80 if rect is None:
81 margin_left = margin_bottom = margin_right = margin_top = None
82 else:
83 margin_left, margin_bottom, _right, _top = rect
84 if _right:
85 margin_right = 1 - _right
86 else:
87 margin_right = None
88 if _top:
89 margin_top = 1 - _top
90 else:
91 margin_top = None
93 vspaces = [[] for i in range((rows + 1) * cols)]
94 hspaces = [[] for i in range(rows * (cols + 1))]
96 union = Bbox.union
98 if ax_bbox_list is None:
99 ax_bbox_list = [
100 union([ax.get_position(original=True) for ax in subplots])
101 for subplots in subplot_list]
103 for subplots, ax_bbox, (num1, num2) in zip(subplot_list,
104 ax_bbox_list,
105 num1num2_list):
106 if all(not ax.get_visible() for ax in subplots):
107 continue
109 tight_bbox_raw = union([ax.get_tightbbox(renderer) for ax in subplots
110 if ax.get_visible()])
111 tight_bbox = TransformedBbox(tight_bbox_raw,
112 fig.transFigure.inverted())
114 row1, col1 = divmod(num1, cols)
116 if num2 is None:
117 # left
118 hspaces[row1 * (cols + 1) + col1].append(
119 _get_left(tight_bbox, ax_bbox))
120 # right
121 hspaces[row1 * (cols + 1) + (col1 + 1)].append(
122 _get_right(tight_bbox, ax_bbox))
123 # top
124 vspaces[row1 * cols + col1].append(
125 _get_top(tight_bbox, ax_bbox))
126 # bottom
127 vspaces[(row1 + 1) * cols + col1].append(
128 _get_bottom(tight_bbox, ax_bbox))
130 else:
131 row2, col2 = divmod(num2, cols)
133 for row_i in range(row1, row2 + 1):
134 # left
135 hspaces[row_i * (cols + 1) + col1].append(
136 _get_left(tight_bbox, ax_bbox))
137 # right
138 hspaces[row_i * (cols + 1) + (col2 + 1)].append(
139 _get_right(tight_bbox, ax_bbox))
140 for col_i in range(col1, col2 + 1):
141 # top
142 vspaces[row1 * cols + col_i].append(
143 _get_top(tight_bbox, ax_bbox))
144 # bottom
145 vspaces[(row2 + 1) * cols + col_i].append(
146 _get_bottom(tight_bbox, ax_bbox))
148 fig_width_inch, fig_height_inch = fig.get_size_inches()
150 # margins can be negative for axes with aspect applied. And we
151 # append + [0] to make minimum margins 0
153 if not margin_left:
154 margin_left = max([sum(s) for s in hspaces[::cols + 1]] + [0])
155 margin_left += pad_inches / fig_width_inch
157 if not margin_right:
158 margin_right = max([sum(s) for s in hspaces[cols::cols + 1]] + [0])
159 margin_right += pad_inches / fig_width_inch
161 if not margin_top:
162 margin_top = max([sum(s) for s in vspaces[:cols]] + [0])
163 margin_top += pad_inches / fig_height_inch
165 if not margin_bottom:
166 margin_bottom = max([sum(s) for s in vspaces[-cols:]] + [0])
167 margin_bottom += pad_inches / fig_height_inch
169 if margin_left + margin_right >= 1:
170 cbook._warn_external('Tight layout not applied. The left and right '
171 'margins cannot be made large enough to '
172 'accommodate all axes decorations. ')
173 return None
174 if margin_bottom + margin_top >= 1:
175 cbook._warn_external('Tight layout not applied. The bottom and top '
176 'margins cannot be made large enough to '
177 'accommodate all axes decorations. ')
178 return None
180 kwargs = dict(left=margin_left,
181 right=1 - margin_right,
182 bottom=margin_bottom,
183 top=1 - margin_top)
184 if cols > 1:
185 hspace = (
186 max(sum(s)
187 for i in range(rows)
188 for s in hspaces[i * (cols + 1) + 1:(i + 1) * (cols + 1) - 1])
189 + hpad_inches / fig_width_inch)
190 # axes widths:
191 h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols
192 if h_axes < 0:
193 cbook._warn_external('Tight layout not applied. tight_layout '
194 'cannot make axes width small enough to '
195 'accommodate all axes decorations')
196 return None
197 else:
198 kwargs["wspace"] = hspace / h_axes
200 if rows > 1:
201 vspace = (max(sum(s) for s in vspaces[cols:-cols])
202 + vpad_inches / fig_height_inch)
203 v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows
204 if v_axes < 0:
205 cbook._warn_external('Tight layout not applied. tight_layout '
206 'cannot make axes height small enough to '
207 'accommodate all axes decorations')
208 return None
209 else:
210 kwargs["hspace"] = vspace / v_axes
212 return kwargs
215def get_renderer(fig):
216 if fig._cachedRenderer:
217 renderer = fig._cachedRenderer
218 else:
219 canvas = fig.canvas
221 if canvas and hasattr(canvas, "get_renderer"):
222 renderer = canvas.get_renderer()
223 else: # Some noninteractive backends have no renderer until draw time.
224 cbook._warn_external("tight_layout: falling back to Agg renderer")
225 from matplotlib.backends.backend_agg import FigureCanvasAgg
226 canvas = FigureCanvasAgg(fig)
227 renderer = canvas.get_renderer()
229 return renderer
232def get_subplotspec_list(axes_list, grid_spec=None):
233 """Return a list of subplotspec from the given list of axes.
235 For an instance of axes that does not support subplotspec, None is inserted
236 in the list.
238 If grid_spec is given, None is inserted for those not from the given
239 grid_spec.
240 """
241 subplotspec_list = []
242 for ax in axes_list:
243 axes_or_locator = ax.get_axes_locator()
244 if axes_or_locator is None:
245 axes_or_locator = ax
247 if hasattr(axes_or_locator, "get_subplotspec"):
248 subplotspec = axes_or_locator.get_subplotspec()
249 subplotspec = subplotspec.get_topmost_subplotspec()
250 gs = subplotspec.get_gridspec()
251 if grid_spec is not None:
252 if gs != grid_spec:
253 subplotspec = None
254 elif gs.locally_modified_subplot_params():
255 subplotspec = None
256 else:
257 subplotspec = None
259 subplotspec_list.append(subplotspec)
261 return subplotspec_list
264def get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer,
265 pad=1.08, h_pad=None, w_pad=None, rect=None):
266 """
267 Return subplot parameters for tight-layouted-figure with specified padding.
269 Parameters
270 ----------
271 fig : Figure
272 axes_list : list of Axes
273 subplotspec_list : list of `.SubplotSpec`
274 The subplotspecs of each axes.
275 renderer : renderer
276 pad : float
277 Padding between the figure edge and the edges of subplots, as a
278 fraction of the font size.
279 h_pad, w_pad : float
280 Padding (height/width) between edges of adjacent subplots. Defaults to
281 *pad*.
282 rect : Tuple[float, float, float, float], optional
283 (left, bottom, right, top) rectangle in normalized figure coordinates
284 that the whole subplots area (including labels) will fit into.
285 Defaults to using the entire figure.
287 Returns
288 -------
289 subplotspec or None
290 subplotspec kwargs to be passed to `.Figure.subplots_adjust` or
291 None if tight_layout could not be accomplished.
293 """
295 subplot_list = []
296 nrows_list = []
297 ncols_list = []
298 ax_bbox_list = []
300 # Multiple axes can share same subplot_interface (e.g., axes_grid1); thus
301 # we need to join them together.
302 subplot_dict = {}
304 subplotspec_list2 = []
306 for ax, subplotspec in zip(axes_list, subplotspec_list):
307 if subplotspec is None:
308 continue
310 subplots = subplot_dict.setdefault(subplotspec, [])
312 if not subplots:
313 myrows, mycols, _, _ = subplotspec.get_geometry()
314 nrows_list.append(myrows)
315 ncols_list.append(mycols)
316 subplotspec_list2.append(subplotspec)
317 subplot_list.append(subplots)
318 ax_bbox_list.append(subplotspec.get_position(fig))
320 subplots.append(ax)
322 if len(nrows_list) == 0 or len(ncols_list) == 0:
323 return {}
325 max_nrows = max(nrows_list)
326 max_ncols = max(ncols_list)
328 num1num2_list = []
329 for subplotspec in subplotspec_list2:
330 rows, cols, num1, num2 = subplotspec.get_geometry()
331 div_row, mod_row = divmod(max_nrows, rows)
332 div_col, mod_col = divmod(max_ncols, cols)
333 if mod_row != 0:
334 cbook._warn_external('tight_layout not applied: number of rows '
335 'in subplot specifications must be '
336 'multiples of one another.')
337 return {}
338 if mod_col != 0:
339 cbook._warn_external('tight_layout not applied: number of '
340 'columns in subplot specifications must be '
341 'multiples of one another.')
342 return {}
344 rowNum1, colNum1 = divmod(num1, cols)
345 if num2 is None:
346 rowNum2, colNum2 = rowNum1, colNum1
347 else:
348 rowNum2, colNum2 = divmod(num2, cols)
350 num1num2_list.append((rowNum1 * div_row * max_ncols +
351 colNum1 * div_col,
352 ((rowNum2 + 1) * div_row - 1) * max_ncols +
353 (colNum2 + 1) * div_col - 1))
355 kwargs = auto_adjust_subplotpars(fig, renderer,
356 nrows_ncols=(max_nrows, max_ncols),
357 num1num2_list=num1num2_list,
358 subplot_list=subplot_list,
359 ax_bbox_list=ax_bbox_list,
360 pad=pad, h_pad=h_pad, w_pad=w_pad)
362 # kwargs can be none if tight_layout fails...
363 if rect is not None and kwargs is not None:
364 # if rect is given, the whole subplots area (including
365 # labels) will fit into the rect instead of the
366 # figure. Note that the rect argument of
367 # *auto_adjust_subplotpars* specify the area that will be
368 # covered by the total area of axes.bbox. Thus we call
369 # auto_adjust_subplotpars twice, where the second run
370 # with adjusted rect parameters.
372 left, bottom, right, top = rect
373 if left is not None:
374 left += kwargs["left"]
375 if bottom is not None:
376 bottom += kwargs["bottom"]
377 if right is not None:
378 right -= (1 - kwargs["right"])
379 if top is not None:
380 top -= (1 - kwargs["top"])
382 kwargs = auto_adjust_subplotpars(fig, renderer,
383 nrows_ncols=(max_nrows, max_ncols),
384 num1num2_list=num1num2_list,
385 subplot_list=subplot_list,
386 ax_bbox_list=ax_bbox_list,
387 pad=pad, h_pad=h_pad, w_pad=w_pad,
388 rect=(left, bottom, right, top))
390 return kwargs