Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8f20012

Browse files
committed
FIX: let colorbars for CL be placed arbitrarily in axes grid
1 parent 8c99daf commit 8f20012

File tree

5 files changed

+147
-55
lines changed

5 files changed

+147
-55
lines changed

lib/matplotlib/_constrained_layout.py

Lines changed: 93 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,36 @@ def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
555555
return lb, lbpos
556556

557557

558+
def _getmaxminrowcolumn(axs):
559+
# helper to get the min/max rows and columns of a list of axes.
560+
maxrow = -100000
561+
minrow = 1000000
562+
maxax = None
563+
minax = None
564+
maxcol = -100000
565+
mincol = 1000000
566+
maxax_col = None
567+
minax_col = None
568+
569+
for ax in axs:
570+
subspec = ax.get_subplotspec()
571+
nrows, ncols, row_start, row_stop, col_start, col_stop = \
572+
subspec.get_rows_columns()
573+
if row_stop > maxrow:
574+
maxrow = row_stop
575+
maxax = ax
576+
if row_start < minrow:
577+
minrow = row_start
578+
minax = ax
579+
if col_stop > maxcol:
580+
maxcol = col_stop
581+
maxax_col = ax
582+
if col_start < mincol:
583+
mincol = col_start
584+
minax_col = ax
585+
return (minrow, maxrow, minax, maxax, mincol, maxcol, minax_col, maxax_col)
586+
587+
558588
def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
559589
"""
560590
Do the layout for a colorbar, to not oeverly pollute colorbar.py
@@ -569,6 +599,10 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
569599
lb = layoutbox.LayoutBox(parent=gslb.parent,
570600
name=gslb.parent.name + '.cbar',
571601
artist=cax)
602+
# figure out the row and column extent of the parents.
603+
(minrow, maxrow, minax_row, maxax_row,
604+
mincol, maxcol, minax_col, maxax_col) = _getmaxminrowcolumn(parents)
605+
572606
if location in ('left', 'right'):
573607
lbpos = layoutbox.LayoutBox(
574608
parent=lb,
@@ -577,39 +611,43 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
577611
pos=True,
578612
subplot=False,
579613
artist=cax)
580-
581-
if location == 'right':
582-
# arrange to right of the gridpec sibbling
583-
layoutbox.hstack([gslb, lb], padding=pad * gslb.width,
584-
strength='strong')
585-
else:
586-
layoutbox.hstack([lb, gslb], padding=pad * gslb.width)
614+
for ax in parents:
615+
if location == 'right':
616+
order = [ax._layoutbox, lb]
617+
else:
618+
order = [lb, ax._layoutbox]
619+
layoutbox.hstack(order, padding=pad * gslb.width,
620+
strength='strong')
587621
# constrain the height and center...
588622
# This isn't quite right. We'd like the colorbar
589623
# pos to line up w/ the axes poss, not the size of the
590624
# gs.
591-
maxrow = -100000
592-
minrow = 1000000
593-
maxax = None
594-
minax = None
595625

596-
for ax in parents:
597-
subspec = ax.get_subplotspec()
598-
nrows, ncols = subspec.get_gridspec().get_geometry()
599-
for num in [subspec.num1, subspec.num2]:
600-
rownum1, colnum1 = divmod(subspec.num1, ncols)
601-
if rownum1 > maxrow:
602-
maxrow = rownum1
603-
maxax = ax
604-
if rownum1 < minrow:
605-
minrow = rownum1
606-
minax = ax
607-
# invert the order so these are bottom to top:
608-
maxposlb = minax._poslayoutbox
609-
minposlb = maxax._poslayoutbox
626+
# Horizontal Layout: need to check all the axes in this gridspec
627+
for ch in gslb.children:
628+
subspec = ch.artist
629+
nrows, ncols, row_start, row_stop, col_start, col_stop = \
630+
subspec.get_rows_columns()
631+
if location == 'right':
632+
if col_stop <= maxcol:
633+
order = [subspec._layoutbox, lb]
634+
# arrange to right of the parents
635+
if col_start > maxcol:
636+
order = [lb, subspec._layoutbox]
637+
elif location == 'left':
638+
if col_start >= mincol:
639+
order = [lb, subspec._layoutbox]
640+
if col_stop < mincol:
641+
order = [subspec._layoutbox, lb]
642+
layoutbox.hstack(order, padding=pad * gslb.width,
643+
strength='strong')
644+
645+
# Vertical layout:
646+
maxposlb = minax_row._poslayoutbox
647+
minposlb = maxax_row._poslayoutbox
610648
# now we want the height of the colorbar pos to be
611-
# set by the top and bottom of these poss
612-
# bottom top
649+
# set by the top and bottom of the min/max axes...
650+
# bottom top
613651
# b t
614652
# h = (top-bottom)*shrink
615653
# b = bottom + (top-bottom - h) / 2.
@@ -633,29 +671,35 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
633671
subplot=False,
634672
artist=cax)
635673

636-
if location == 'bottom':
637-
layoutbox.vstack([gslb, lb], padding=pad * gslb.width)
638-
else:
639-
layoutbox.vstack([lb, gslb], padding=pad * gslb.width)
640-
641-
maxcol = -100000
642-
mincol = 1000000
643-
maxax = None
644-
minax = None
645-
646674
for ax in parents:
647-
subspec = ax.get_subplotspec()
648-
nrows, ncols = subspec.get_gridspec().get_geometry()
649-
for num in [subspec.num1, subspec.num2]:
650-
rownum1, colnum1 = divmod(subspec.num1, ncols)
651-
if colnum1 > maxcol:
652-
maxcol = colnum1
653-
maxax = ax
654-
if rownum1 < mincol:
655-
mincol = colnum1
656-
minax = ax
657-
maxposlb = maxax._poslayoutbox
658-
minposlb = minax._poslayoutbox
675+
if location == 'bottom':
676+
order = [ax._layoutbox, lb]
677+
else:
678+
order = [lb, ax._layoutbox]
679+
layoutbox.vstack(order, padding=pad * gslb.width,
680+
strength='strong')
681+
682+
# Vertical Layout: need to check all the axes in this gridspec
683+
for ch in gslb.children:
684+
subspec = ch.artist
685+
nrows, ncols, row_start, row_stop, col_start, col_stop = \
686+
subspec.get_rows_columns()
687+
if location == 'bottom':
688+
if row_stop <= minrow:
689+
order = [subspec._layoutbox, lb]
690+
if row_start > maxrow:
691+
order = [lb, subspec._layoutbox]
692+
elif location == 'top':
693+
if row_stop < minrow:
694+
order = [subspec._layoutbox, lb]
695+
if row_start >= maxrow:
696+
order = [lb, subspec._layoutbox]
697+
layoutbox.vstack(order, padding=pad * gslb.width,
698+
strength='strong')
699+
700+
# Do horizontal layout...
701+
maxposlb = maxax_col._poslayoutbox
702+
minposlb = minax_col._poslayoutbox
659703
lbpos.constrain_width((maxposlb.right - minposlb.left) *
660704
shrink)
661705
lbpos.constrain_left(

lib/matplotlib/colorbar.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,7 @@ def make_axes(parents, location=None, orientation=None, fraction=0.15,
12421242
Returns (cax, kw), the child axes and the reduced kw dictionary to be
12431243
passed when creating the colorbar instance.
12441244
'''
1245+
12451246
locations = ["left", "right", "top", "bottom"]
12461247
if orientation is not None and location is not None:
12471248
raise TypeError('position and orientation are mutually exclusive. '

lib/matplotlib/tests/test_constrainedlayout.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,24 @@ def test_constrained_layout23():
378378
for i in range(2):
379379
fig, ax = plt.subplots(num="123", constrained_layout=True, clear=True)
380380
fig.suptitle("Suptitle{}".format(i))
381+
382+
383+
@image_comparison(baseline_images=['test_colorbar_location'],
384+
extensions=['png'], remove_text=True, style='mpl20')
385+
def test_colorbar_location():
386+
"""
387+
Test that colorbar handling is as expected for various complicated
388+
cases...
389+
"""
390+
391+
fig, axs = plt.subplots(4, 5, constrained_layout=True)
392+
for ax in axs.flatten():
393+
pcm = example_pcolor(ax)
394+
ax.set_xlabel('')
395+
ax.set_ylabel('')
396+
fig.colorbar(pcm, ax=axs[:, 1], shrink=0.4)
397+
fig.colorbar(pcm, ax=axs[-1, :2], shrink=0.5, location='bottom')
398+
fig.colorbar(pcm, ax=axs[0, 2:], shrink=0.5, location='bottom')
399+
fig.colorbar(pcm, ax=axs[-2, 3:], shrink=0.5, location='top')
400+
fig.colorbar(pcm, ax=axs[0, 0], shrink=0.5, location='left')
401+
fig.colorbar(pcm, ax=axs[1:3, 2], shrink=0.5, location='right')

tutorials/intermediate/constrainedlayout_guide.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,10 @@ def example_plot(ax, fontsize=12, nodec=False):
118118
#
119119
# .. note::
120120
#
121-
# For the `~.axes.Axes.pcolormesh` kwargs (``pc_kwargs``) we use a dictionary.
122-
# Below we will assign one colorbar to a number of axes each containing
123-
# a `~.cm.ScalarMappable`; specifying the norm and colormap ensures
124-
# the colorbar is accurate for all the axes.
121+
# For the `~.axes.Axes.pcolormesh` kwargs (``pc_kwargs``) we use a
122+
# dictionary. Below we will assign one colorbar to a number of axes each
123+
# containing a `~.cm.ScalarMappable`; specifying the norm and colormap
124+
# ensuresthe colorbar is accurate for all the axes.
125125

126126
arr = np.arange(100).reshape((10, 10))
127127
norm = mcolors.Normalize(vmin=0., vmax=100.)
@@ -133,14 +133,25 @@ def example_plot(ax, fontsize=12, nodec=False):
133133

134134
############################################################################
135135
# If you specify a list of axes (or other iterable container) to the
136-
# ``ax`` argument of ``colorbar``, constrained_layout will take space from all
137-
# axes that share the same gridspec.
136+
# ``ax`` argument of ``colorbar``, constrained_layout will take space from
137+
# the specified axes.
138138

139139
fig, axs = plt.subplots(2, 2, figsize=(4, 4), constrained_layout=True)
140140
for ax in axs.flatten():
141141
im = ax.pcolormesh(arr, **pc_kwargs)
142142
fig.colorbar(im, ax=axs, shrink=0.6)
143143

144+
############################################################################
145+
# If you specify a list of axes from inside a grid of axes, the colorbar
146+
# will steal space appropriately, and leave a gap, but all subplots will
147+
# still be the same size.
148+
149+
fig, axs = plt.subplots(3, 3, figsize=(4, 4), constrained_layout=True)
150+
for ax in axs.flatten():
151+
im = ax.pcolormesh(arr, **pc_kwargs)
152+
fig.colorbar(im, ax=axs[1:, ][:, 1], shrink=0.8)
153+
fig.colorbar(im, ax=axs[:, -1], shrink=0.6)
154+
144155
############################################################################
145156
# Note that there is a bit of a subtlety when specifying a single axes
146157
# as the parent. In the following, it might be desirable and expected
@@ -455,6 +466,21 @@ def docomplicated(suptitle=None):
455466
ax2 = fig.add_axes(bb_ax2)
456467

457468
###############################################################################
469+
# Manually turning off ``constrained_layout``
470+
# ===========================================
471+
#
472+
# ``constrained_layout`` usually adjusts the axes positions on each draw
473+
# of the figure. If you want to get the spacing provided by
474+
# ``constrained_layout`` but then not have it update, then do the initial
475+
# draw and then call ``fig.set_constrained_layout(False)``.
476+
# This is potentially useful for animations where the tick labels may
477+
# change length.
478+
#
479+
# Note that ``constrained_layout`` is turned off for ``ZOOM`` and ``PAN``
480+
# GUI events for the backends that use the toolbar. This prevents the
481+
# axes from changing position during zooming and panning.
482+
#
483+
#
458484
# Limitations
459485
# ========================
460486
#

0 commit comments

Comments
 (0)