1"""
2================================
3Constrained Layout Guide
4================================
5
6How to use constrained-layout to fit plots within your figure cleanly.
7
8*constrained_layout* automatically adjusts subplots and decorations like
9legends and colorbars so that they fit in the figure window while still
10preserving, as best they can, the logical layout requested by the user.
11
12*constrained_layout* is similar to
13:doc:`tight_layout</tutorials/intermediate/tight_layout_guide>`,
14but uses a constraint solver to determine the size of axes that allows
15them to fit.
16
17*constrained_layout* needs to be activated before any axes are added to
18a figure. Two ways of doing so are
19
20* using the respective argument to :func:`~.pyplot.subplots` or
21  :func:`~.pyplot.figure`, e.g.::
22
23      plt.subplots(constrained_layout=True)
24
25* activate it via :ref:`rcParams<matplotlib-rcparams>`, like::
26
27      plt.rcParams['figure.constrained_layout.use'] = True
28
29Those are described in detail throughout the following sections.
30
31.. warning::
32
33    Currently Constrained Layout is **experimental**.  The
34    behaviour and API are subject to change, or the whole functionality
35    may be removed without a deprecation period.  If you *require* your
36    plots to be absolutely reproducible, get the Axes positions after
37    running Constrained Layout and use ``ax.set_position()`` in your code
38    with ``constrained_layout=False``.
39
40Simple Example
41==============
42
43In Matplotlib, the location of axes (including subplots) are specified in
44normalized figure coordinates. It can happen that your axis labels or
45titles (or sometimes even ticklabels) go outside the figure area, and are thus
46clipped.
47"""
48
49# sphinx_gallery_thumbnail_number = 18
50
51
52import matplotlib.pyplot as plt
53import matplotlib.colors as mcolors
54import matplotlib.gridspec as gridspec
55import numpy as np
56
57plt.rcParams['savefig.facecolor'] = "0.8"
58plt.rcParams['figure.figsize'] = 4.5, 4.
59plt.rcParams['figure.max_open_warning'] = 50
60
61
62def example_plot(ax, fontsize=12, hide_labels=False):
63    ax.plot([1, 2])
64
65    ax.locator_params(nbins=3)
66    if hide_labels:
67        ax.set_xticklabels([])
68        ax.set_yticklabels([])
69    else:
70        ax.set_xlabel('x-label', fontsize=fontsize)
71        ax.set_ylabel('y-label', fontsize=fontsize)
72        ax.set_title('Title', fontsize=fontsize)
73
74
75fig, ax = plt.subplots(constrained_layout=False)
76example_plot(ax, fontsize=24)
77
78###############################################################################
79# To prevent this, the location of axes needs to be adjusted. For
80# subplots, this can be done by adjusting the subplot params
81# (:ref:`howto-subplots-adjust`). However, specifying your figure with the
82# ``constrained_layout=True`` kwarg will do the adjusting automatically.
83
84fig, ax = plt.subplots(constrained_layout=True)
85example_plot(ax, fontsize=24)
86
87###############################################################################
88# When you have multiple subplots, often you see labels of different
89# axes overlapping each other.
90
91fig, axs = plt.subplots(2, 2, constrained_layout=False)
92for ax in axs.flat:
93    example_plot(ax)
94
95###############################################################################
96# Specifying ``constrained_layout=True`` in the call to ``plt.subplots``
97# causes the layout to be properly constrained.
98
99fig, axs = plt.subplots(2, 2, constrained_layout=True)
100for ax in axs.flat:
101    example_plot(ax)
102
103###############################################################################
104# Colorbars
105# =========
106#
107# If you create a colorbar with `.Figure.colorbar`,
108# you need to make room for it.  ``constrained_layout`` does this
109# automatically.  Note that if you specify ``use_gridspec=True`` it will be
110# ignored because this option is made for improving the layout via
111# ``tight_layout``.
112#
113# .. note::
114#
115#   For the `~.axes.Axes.pcolormesh` kwargs (``pc_kwargs``) we use a
116#   dictionary. Below we will assign one colorbar to a number of axes each
117#   containing a `~.cm.ScalarMappable`; specifying the norm and colormap
118#   ensures the colorbar is accurate for all the axes.
119
120arr = np.arange(100).reshape((10, 10))
121norm = mcolors.Normalize(vmin=0., vmax=100.)
122# see note above: this makes all pcolormesh calls consistent:
123pc_kwargs = {'rasterized': True, 'cmap': 'viridis', 'norm': norm}
124fig, ax = plt.subplots(figsize=(4, 4), constrained_layout=True)
125im = ax.pcolormesh(arr, **pc_kwargs)
126fig.colorbar(im, ax=ax, shrink=0.6)
127
128############################################################################
129# If you specify a list of axes (or other iterable container) to the
130# ``ax`` argument of ``colorbar``, constrained_layout will take space from
131# the specified axes.
132
133fig, axs = plt.subplots(2, 2, figsize=(4, 4), constrained_layout=True)
134for ax in axs.flat:
135    im = ax.pcolormesh(arr, **pc_kwargs)
136fig.colorbar(im, ax=axs, shrink=0.6)
137
138############################################################################
139# If you specify a list of axes from inside a grid of axes, the colorbar
140# will steal space appropriately, and leave a gap, but all subplots will
141# still be the same size.
142
143fig, axs = plt.subplots(3, 3, figsize=(4, 4), constrained_layout=True)
144for ax in axs.flat:
145    im = ax.pcolormesh(arr, **pc_kwargs)
146fig.colorbar(im, ax=axs[1:, ][:, 1], shrink=0.8)
147fig.colorbar(im, ax=axs[:, -1], shrink=0.6)
148
149####################################################
150# Suptitle
151# =========
152#
153# ``constrained_layout`` can also make room for `~.figure.Figure.suptitle`.
154
155fig, axs = plt.subplots(2, 2, figsize=(4, 4), constrained_layout=True)
156for ax in axs.flat:
157    im = ax.pcolormesh(arr, **pc_kwargs)
158fig.colorbar(im, ax=axs, shrink=0.6)
159fig.suptitle('Big Suptitle')
160
161####################################################
162# Legends
163# =======
164#
165# Legends can be placed outside of their parent axis.
166# Constrained-layout is designed to handle this for :meth:`.Axes.legend`.
167# However, constrained-layout does *not* handle legends being created via
168# :meth:`.Figure.legend` (yet).
169
170fig, ax = plt.subplots(constrained_layout=True)
171ax.plot(np.arange(10), label='This is a plot')
172ax.legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
173
174#############################################
175# However, this will steal space from a subplot layout:
176
177fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
178axs[0].plot(np.arange(10))
179axs[1].plot(np.arange(10), label='This is a plot')
180axs[1].legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
181
182#############################################
183# In order for a legend or other artist to *not* steal space
184# from the subplot layout, we can ``leg.set_in_layout(False)``.
185# Of course this can mean the legend ends up
186# cropped, but can be useful if the plot is subsequently called
187# with ``fig.savefig('outname.png', bbox_inches='tight')``.  Note,
188# however, that the legend's ``get_in_layout`` status will have to be
189# toggled again to make the saved file work, and we must manually
190# trigger a draw if we want constrained_layout to adjust the size
191# of the axes before printing.
192
193fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
194
195axs[0].plot(np.arange(10))
196axs[1].plot(np.arange(10), label='This is a plot')
197leg = axs[1].legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
198leg.set_in_layout(False)
199# trigger a draw so that constrained_layout is executed once
200# before we turn it off when printing....
201fig.canvas.draw()
202# we want the legend included in the bbox_inches='tight' calcs.
203leg.set_in_layout(True)
204# we don't want the layout to change at this point.
205fig.set_constrained_layout(False)
206fig.savefig('../../doc/_static/constrained_layout_1b.png',
207            bbox_inches='tight', dpi=100)
208
209#############################################
210# The saved file looks like:
211#
212# .. image:: /_static/constrained_layout_1b.png
213#    :align: center
214#
215# A better way to get around this awkwardness is to simply
216# use the legend method provided by `.Figure.legend`:
217fig, axs = plt.subplots(1, 2, figsize=(4, 2), constrained_layout=True)
218axs[0].plot(np.arange(10))
219lines = axs[1].plot(np.arange(10), label='This is a plot')
220labels = [l.get_label() for l in lines]
221leg = fig.legend(lines, labels, loc='center left',
222                 bbox_to_anchor=(0.8, 0.5), bbox_transform=axs[1].transAxes)
223fig.savefig('../../doc/_static/constrained_layout_2b.png',
224            bbox_inches='tight', dpi=100)
225
226#############################################
227# The saved file looks like:
228#
229# .. image:: /_static/constrained_layout_2b.png
230#    :align: center
231#
232
233###############################################################################
234# Padding and Spacing
235# ===================
236#
237# Padding between axes is controlled in the horizontal by *w_pad* and
238# *wspace*, and vertical by *h_pad* and *hspace*.  These can be edited
239# via `~.figure.Figure.set_constrained_layout_pads`.  *w/h_pad* are
240# the minimum space around the axes in units of inches:
241
242fig, axs = plt.subplots(2, 2, constrained_layout=True)
243for ax in axs.flat:
244    example_plot(ax, hide_labels=True)
245fig.set_constrained_layout_pads(w_pad=4 / 72, h_pad=4 / 72, hspace=0, wspace=0)
246
247##########################################
248# Spacing between subplots is further set by *wspace* and *hspace*. These
249# are specified as a fraction of the size of the subplot group as a whole.
250# If these values are smaller than *w_pad* or *h_pad*, then the fixed pads are
251# used instead. Note in the below how the space at the edges doesn't change
252# from the above, but the space between subplots does.
253
254fig, axs = plt.subplots(2, 2, constrained_layout=True)
255for ax in axs.flat:
256    example_plot(ax, hide_labels=True)
257fig.set_constrained_layout_pads(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2,
258                                wspace=0.2)
259
260##########################################
261# If there are more than two columns, the *wspace* is shared between them,
262# so here the wspace is divided in 2, with a *wspace* of 0.1 between each
263# column:
264
265fig, axs = plt.subplots(2, 3, constrained_layout=True)
266for ax in axs.flat:
267    example_plot(ax, hide_labels=True)
268fig.set_constrained_layout_pads(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2,
269                                wspace=0.2)
270
271##########################################
272# GridSpecs also have optional *hspace* and *wspace* keyword arguments,
273# that will be used instead of the pads set by ``constrained_layout``:
274
275fig, axs = plt.subplots(2, 2, constrained_layout=True,
276                        gridspec_kw={'wspace': 0.3, 'hspace': 0.2})
277for ax in axs.flat:
278    example_plot(ax, hide_labels=True)
279# this has no effect because the space set in the gridspec trumps the
280# space set in constrained_layout.
281fig.set_constrained_layout_pads(w_pad=4 / 72, h_pad=4 / 72, hspace=0.0,
282                                wspace=0.0)
283plt.show()
284
285##########################################
286# Spacing with colorbars
287# -----------------------
288#
289# Colorbars are placed a distance *pad* from their parent, where *pad*
290# is a fraction of the width of the parent(s).  The spacing to the
291# next subplot is then given by *w/hspace*.
292
293fig, axs = plt.subplots(2, 2, constrained_layout=True)
294pads = [0, 0.05, 0.1, 0.2]
295for pad, ax in zip(pads, axs.flat):
296    pc = ax.pcolormesh(arr, **pc_kwargs)
297    fig.colorbar(pc, ax=ax, shrink=0.6, pad=pad)
298    ax.set_xticklabels('')
299    ax.set_yticklabels('')
300    ax.set_title(f'pad: {pad}')
301fig.set_constrained_layout_pads(w_pad=2 / 72, h_pad=2 / 72, hspace=0.2,
302                                wspace=0.2)
303
304##########################################
305# rcParams
306# ========
307#
308# There are five :ref:`rcParams<matplotlib-rcparams>` that can be set,
309# either in a script or in the :file:`matplotlibrc` file.
310# They all have the prefix ``figure.constrained_layout``:
311#
312# - *use*: Whether to use constrained_layout. Default is False
313# - *w_pad*, *h_pad*:    Padding around axes objects.
314#   Float representing inches.  Default is 3./72. inches (3 pts)
315# - *wspace*, *hspace*:  Space between subplot groups.
316#   Float representing a fraction of the subplot widths being separated.
317#   Default is 0.02.
318
319plt.rcParams['figure.constrained_layout.use'] = True
320fig, axs = plt.subplots(2, 2, figsize=(3, 3))
321for ax in axs.flat:
322    example_plot(ax)
323
324#############################
325# Use with GridSpec
326# =================
327#
328# constrained_layout is meant to be used
329# with :func:`~matplotlib.figure.Figure.subplots` or
330# :func:`~matplotlib.gridspec.GridSpec` and
331# :func:`~matplotlib.figure.Figure.add_subplot`.
332#
333# Note that in what follows ``constrained_layout=True``
334
335fig = plt.figure()
336
337gs1 = gridspec.GridSpec(2, 1, figure=fig)
338ax1 = fig.add_subplot(gs1[0])
339ax2 = fig.add_subplot(gs1[1])
340
341example_plot(ax1)
342example_plot(ax2)
343
344###############################################################################
345# More complicated gridspec layouts are possible.  Note here we use the
346# convenience functions `~.Figure.add_gridspec` and
347# `~.SubplotSpec.subgridspec`.
348
349fig = plt.figure()
350
351gs0 = fig.add_gridspec(1, 2)
352
353gs1 = gs0[0].subgridspec(2, 1)
354ax1 = fig.add_subplot(gs1[0])
355ax2 = fig.add_subplot(gs1[1])
356
357example_plot(ax1)
358example_plot(ax2)
359
360gs2 = gs0[1].subgridspec(3, 1)
361
362for ss in gs2:
363    ax = fig.add_subplot(ss)
364    example_plot(ax)
365    ax.set_title("")
366    ax.set_xlabel("")
367
368ax.set_xlabel("x-label", fontsize=12)
369
370############################################################################
371# Note that in the above the left and right columns don't have the same
372# vertical extent.  If we want the top and bottom of the two grids to line up
373# then they need to be in the same gridspec.  We need to make this figure
374# larger as well in order for the axes not to collapse to zero height:
375
376fig = plt.figure(figsize=(4, 6))
377
378gs0 = fig.add_gridspec(6, 2)
379
380ax1 = fig.add_subplot(gs0[:3, 0])
381ax2 = fig.add_subplot(gs0[3:, 0])
382
383example_plot(ax1)
384example_plot(ax2)
385
386ax = fig.add_subplot(gs0[0:2, 1])
387example_plot(ax, hide_labels=True)
388ax = fig.add_subplot(gs0[2:4, 1])
389example_plot(ax, hide_labels=True)
390ax = fig.add_subplot(gs0[4:, 1])
391example_plot(ax, hide_labels=True)
392fig.suptitle('Overlapping Gridspecs')
393
394
395############################################################################
396# This example uses two gridspecs to have the colorbar only pertain to
397# one set of pcolors.  Note how the left column is wider than the
398# two right-hand columns because of this.  Of course, if you wanted the
399# subplots to be the same size you only needed one gridspec.
400
401
402def docomplicated(suptitle=None):
403    fig = plt.figure()
404    gs0 = fig.add_gridspec(1, 2, figure=fig, width_ratios=[1., 2.])
405    gsl = gs0[0].subgridspec(2, 1)
406    gsr = gs0[1].subgridspec(2, 2)
407
408    for gs in gsl:
409        ax = fig.add_subplot(gs)
410        example_plot(ax)
411    axs = []
412    for gs in gsr:
413        ax = fig.add_subplot(gs)
414        pcm = ax.pcolormesh(arr, **pc_kwargs)
415        ax.set_xlabel('x-label')
416        ax.set_ylabel('y-label')
417        ax.set_title('title')
418
419        axs += [ax]
420    fig.colorbar(pcm, ax=axs)
421    if suptitle is not None:
422        fig.suptitle(suptitle)
423
424
425docomplicated()
426
427###############################################################################
428# Manually setting axes positions
429# ================================
430#
431# There can be good reasons to manually set an axes position.  A manual call
432# to `~.axes.Axes.set_position` will set the axes so constrained_layout has
433# no effect on it anymore. (Note that ``constrained_layout`` still leaves the
434# space for the axes that is moved).
435
436fig, axs = plt.subplots(1, 2)
437example_plot(axs[0], fontsize=12)
438axs[1].set_position([0.2, 0.2, 0.4, 0.4])
439
440###############################################################################
441# Manually turning off ``constrained_layout``
442# ===========================================
443#
444# ``constrained_layout`` usually adjusts the axes positions on each draw
445# of the figure.  If you want to get the spacing provided by
446# ``constrained_layout`` but not have it update, then do the initial
447# draw and then call ``fig.set_constrained_layout(False)``.
448# This is potentially useful for animations where the tick labels may
449# change length.
450#
451# Note that ``constrained_layout`` is turned off for ``ZOOM`` and ``PAN``
452# GUI events for the backends that use the toolbar.  This prevents the
453# axes from changing position during zooming and panning.
454#
455#
456# Limitations
457# ===========
458#
459# Incompatible functions
460# ----------------------
461#
462# ``constrained_layout`` will work with `.pyplot.subplot`, but only if the
463# number of rows and columns is the same for each call.
464# The reason is that each call to `.pyplot.subplot` will create a new
465# `.GridSpec` instance if the geometry is not the same, and
466# ``constrained_layout``.  So the following works fine:
467
468fig = plt.figure()
469
470ax1 = plt.subplot(2, 2, 1)
471ax2 = plt.subplot(2, 2, 3)
472# third axes that spans both rows in second column:
473ax3 = plt.subplot(2, 2, (2, 4))
474
475example_plot(ax1)
476example_plot(ax2)
477example_plot(ax3)
478plt.suptitle('Homogenous nrows, ncols')
479
480###############################################################################
481# but the following leads to a poor layout:
482
483fig = plt.figure()
484
485ax1 = plt.subplot(2, 2, 1)
486ax2 = plt.subplot(2, 2, 3)
487ax3 = plt.subplot(1, 2, 2)
488
489example_plot(ax1)
490example_plot(ax2)
491example_plot(ax3)
492plt.suptitle('Mixed nrows, ncols')
493
494###############################################################################
495# Similarly,
496# `~matplotlib.pyplot.subplot2grid` works with the same limitation
497# that nrows and ncols cannot change for the layout to look good.
498
499fig = plt.figure()
500
501ax1 = plt.subplot2grid((3, 3), (0, 0))
502ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
503ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
504ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
505
506example_plot(ax1)
507example_plot(ax2)
508example_plot(ax3)
509example_plot(ax4)
510fig.suptitle('subplot2grid')
511
512###############################################################################
513# Other Caveats
514# -------------
515#
516# * ``constrained_layout`` only considers ticklabels, axis labels, titles, and
517#   legends.  Thus, other artists may be clipped and also may overlap.
518#
519# * It assumes that the extra space needed for ticklabels, axis labels,
520#   and titles is independent of original location of axes. This is
521#   often true, but there are rare cases where it is not.
522#
523# * There are small differences in how the backends handle rendering fonts,
524#   so the results will not be pixel-identical.
525#
526# * An artist using axes coordinates that extend beyond the axes
527#   boundary will result in unusual layouts when added to an
528#   axes. This can be avoided by adding the artist directly to the
529#   :class:`~matplotlib.figure.Figure` using
530#   :meth:`~matplotlib.figure.Figure.add_artist`. See
531#   :class:`~matplotlib.patches.ConnectionPatch` for an example.
532
533###########################################################
534# Debugging
535# =========
536#
537# Constrained-layout can fail in somewhat unexpected ways.  Because it uses
538# a constraint solver the solver can find solutions that are mathematically
539# correct, but that aren't at all what the user wants.  The usual failure
540# mode is for all sizes to collapse to their smallest allowable value. If
541# this happens, it is for one of two reasons:
542#
543# 1. There was not enough room for the elements you were requesting to draw.
544# 2. There is a bug - in which case open an issue at
545#    https://github.com/matplotlib/matplotlib/issues.
546#
547# If there is a bug, please report with a self-contained example that does
548# not require outside data or dependencies (other than numpy).
549
550###########################################################
551# Notes on the algorithm
552# ======================
553#
554# The algorithm for the constraint is relatively straightforward, but
555# has some complexity due to the complex ways we can layout a figure.
556#
557# Layout in Matplotlib is carried out with gridspecs
558# via the `~.GridSpec` class. A gridspec is a logical division of the figure
559# into rows and columns, with the relative width of the Axes in those
560# rows and columns set by *width_ratios* and *height_ratios*.
561#
562# In constrained_layout, each gridspec gets a *layoutgrid* associated with
563# it.  The *layoutgrid* has a series of ``left`` and ``right`` variables
564# for each column, and ``bottom`` and ``top`` variables for each row, and
565# further it has a margin for each of left, right, bottom and top.  In each
566# row, the bottom/top margins are widened until all the decorators
567# in that row are accommodated.  Similarly for columns and the left/right
568# margins.
569#
570#
571# Simple case: one Axes
572# ---------------------
573#
574# For a single Axes the layout is straight forward.  There is one parent
575# layoutgrid for the figure consisting of one column and row, and
576# a child layoutgrid for the gridspec that contains the axes, again
577# consisting of one row and column. Space is made for the "decorations" on
578# each side of the axes.  In the code, this is accomplished by the entries in
579# ``do_constrained_layout()`` like::
580#
581#     gridspec._layoutgrid[0, 0].edit_margin_min('left',
582#           -bbox.x0 + pos.x0 + w_pad)
583#
584# where ``bbox`` is the tight bounding box of the axes, and ``pos`` its
585# position.  Note how the four margins encompass the axes decorations.
586
587from matplotlib._layoutgrid import plot_children
588
589fig, ax = plt.subplots(constrained_layout=True)
590example_plot(ax, fontsize=24)
591plot_children(fig, fig._layoutgrid)
592
593#######################################################################
594# Simple case: two Axes
595# ---------------------
596# When there are multiple axes they have their layouts bound in
597# simple ways.  In this example the left axes has much larger decorations
598# than the right, but they share a bottom margin, which is made large
599# enough to accommodate the larger xlabel.   Same with the shared top
600# margin.  The left and right margins are not shared, and hence are
601# allowed to be different.
602
603fig, ax = plt.subplots(1, 2, constrained_layout=True)
604example_plot(ax[0], fontsize=32)
605example_plot(ax[1], fontsize=8)
606plot_children(fig, fig._layoutgrid, printit=False)
607
608#######################################################################
609# Two Axes and colorbar
610# ---------------------
611#
612# A colorbar is simply another item that expands the margin of the parent
613# layoutgrid cell:
614
615fig, ax = plt.subplots(1, 2, constrained_layout=True)
616im = ax[0].pcolormesh(arr, **pc_kwargs)
617fig.colorbar(im, ax=ax[0], shrink=0.6)
618im = ax[1].pcolormesh(arr, **pc_kwargs)
619plot_children(fig, fig._layoutgrid)
620
621#######################################################################
622# Colorbar associated with a Gridspec
623# -----------------------------------
624#
625# If a colorbar belongs to more than one cell of the grid, then
626# it makes a larger margin for each:
627
628fig, axs = plt.subplots(2, 2, constrained_layout=True)
629for ax in axs.flat:
630    im = ax.pcolormesh(arr, **pc_kwargs)
631fig.colorbar(im, ax=axs, shrink=0.6)
632plot_children(fig, fig._layoutgrid, printit=False)
633
634#######################################################################
635# Uneven sized Axes
636# -----------------
637#
638# There are two ways to make axes have an uneven size in a
639# Gridspec layout, either by specifying them to cross Gridspecs rows
640# or columns, or by specifying width and height ratios.
641#
642# The first method is used here.  Note that the middle ``top`` and
643# ``bottom`` margins are not affected by the left-hand column.  This
644# is a conscious decision of the algorithm, and leads to the case where
645# the two right-hand axes have the same height, but it is not 1/2 the height
646# of the left-hand axes.  This is consietent with how ``gridspec`` works
647# without constrained layout.
648
649fig = plt.figure(constrained_layout=True)
650gs = gridspec.GridSpec(2, 2, figure=fig)
651ax = fig.add_subplot(gs[:, 0])
652im = ax.pcolormesh(arr, **pc_kwargs)
653ax = fig.add_subplot(gs[0, 1])
654im = ax.pcolormesh(arr, **pc_kwargs)
655ax = fig.add_subplot(gs[1, 1])
656im = ax.pcolormesh(arr, **pc_kwargs)
657plot_children(fig, fig._layoutgrid, printit=False)
658
659#######################################################################
660# One case that requires finessing is if margins do not have any artists
661# constraining their width. In the case below, the right margin for column 0
662# and the left margin for column 3 have no margin artists to set their width,
663# so we take the maximum width of the margin widths that do have artists.
664# This makes all the axes have the same size:
665
666fig = plt.figure(constrained_layout=True)
667gs = fig.add_gridspec(2, 4)
668ax00 = fig.add_subplot(gs[0, 0:2])
669ax01 = fig.add_subplot(gs[0, 2:])
670ax10 = fig.add_subplot(gs[1, 1:3])
671example_plot(ax10, fontsize=14)
672plot_children(fig, fig._layoutgrid)
673