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