Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf4baa7d

Browse files
ImportanceOfBeingErnestMeeseeksDev[bot]
authored and
MeeseeksDev[bot]
committed
Backport PR#11648: FIX: colorbar placement in constrained layout
1 parent316e173 commitf4baa7d

File tree

7 files changed

+202
-56
lines changed

7 files changed

+202
-56
lines changed

‎.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ per-file-ignores =
9595
tutorials/colors/colormaps.py: E501
9696
tutorials/colors/colors.py: E402
9797
tutorials/intermediate/artists.py: E402, E501
98-
tutorials/intermediate/constrainedlayout_guide.py: E402, E501
98+
tutorials/intermediate/constrainedlayout_guide.py: E402
9999
tutorials/intermediate/gridspec.py: E402, E501
100100
tutorials/intermediate/legend_guide.py: E402, E501
101101
tutorials/intermediate/tight_layout_guide.py: E402, E501
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
=================
3+
Placing Colorbars
4+
=================
5+
6+
Colorbars indicate the quantitative extent of image data. Placing in
7+
a figure is non-trivial because room needs to be made for them.
8+
9+
The simplest case is just attaching a colorbar to each axes:
10+
"""
11+
importmatplotlib.pyplotasplt
12+
importnumpyasnp
13+
14+
fig,axs=plt.subplots(2,2)
15+
cm= ['RdBu_r','viridis']
16+
forcolinrange(2):
17+
forrowinrange(2):
18+
ax=axs[row,col]
19+
pcm=ax.pcolormesh(np.random.random((20,20))* (col+1),
20+
cmap=cm[col])
21+
fig.colorbar(pcm,ax=ax)
22+
plt.show()
23+
24+
######################################################################
25+
# The first column has the same type of data in both rows, so it may
26+
# be desirable to combine the colorbar which we do by calling
27+
# `.Figure.colorbar` with a list of axes instead of a single axes.
28+
29+
fig,axs=plt.subplots(2,2)
30+
cm= ['RdBu_r','viridis']
31+
forcolinrange(2):
32+
forrowinrange(2):
33+
ax=axs[row,col]
34+
pcm=ax.pcolormesh(np.random.random((20,20))* (col+1),
35+
cmap=cm[col])
36+
fig.colorbar(pcm,ax=axs[:,col],shrink=0.6)
37+
plt.show()
38+
39+
######################################################################
40+
# Relatively complicated colorbar layouts are possible using this
41+
# paradigm. Note that this example works far better with
42+
# ``constrained_layout=True``
43+
44+
fig,axs=plt.subplots(3,3,constrained_layout=True)
45+
foraxinaxs.flat:
46+
pcm=ax.pcolormesh(np.random.random((20,20)))
47+
48+
fig.colorbar(pcm,ax=axs[0, :2],shrink=0.6,location='bottom')
49+
fig.colorbar(pcm,ax=[axs[0,2]],location='bottom')
50+
fig.colorbar(pcm,ax=axs[1:, :],location='right',shrink=0.6)
51+
fig.colorbar(pcm,ax=[axs[2,1]],location='left')
52+
53+
54+
plt.show()

‎lib/matplotlib/_constrained_layout.py

Lines changed: 93 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,36 @@ def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
572572
returnlb,lbpos
573573

574574

575+
def_getmaxminrowcolumn(axs):
576+
# helper to get the min/max rows and columns of a list of axes.
577+
maxrow=-100000
578+
minrow=1000000
579+
maxax=None
580+
minax=None
581+
maxcol=-100000
582+
mincol=1000000
583+
maxax_col=None
584+
minax_col=None
585+
586+
foraxinaxs:
587+
subspec=ax.get_subplotspec()
588+
nrows,ncols,row_start,row_stop,col_start,col_stop= \
589+
subspec.get_rows_columns()
590+
ifrow_stop>maxrow:
591+
maxrow=row_stop
592+
maxax=ax
593+
ifrow_start<minrow:
594+
minrow=row_start
595+
minax=ax
596+
ifcol_stop>maxcol:
597+
maxcol=col_stop
598+
maxax_col=ax
599+
ifcol_start<mincol:
600+
mincol=col_start
601+
minax_col=ax
602+
return (minrow,maxrow,minax,maxax,mincol,maxcol,minax_col,maxax_col)
603+
604+
575605
deflayoutcolorbargridspec(parents,cax,shrink,aspect,location,pad=0.05):
576606
"""
577607
Do the layout for a colorbar, to not oeverly pollute colorbar.py
@@ -586,6 +616,10 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
586616
lb=layoutbox.LayoutBox(parent=gslb.parent,
587617
name=gslb.parent.name+'.cbar',
588618
artist=cax)
619+
# figure out the row and column extent of the parents.
620+
(minrow,maxrow,minax_row,maxax_row,
621+
mincol,maxcol,minax_col,maxax_col)=_getmaxminrowcolumn(parents)
622+
589623
iflocationin ('left','right'):
590624
lbpos=layoutbox.LayoutBox(
591625
parent=lb,
@@ -594,39 +628,43 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
594628
pos=True,
595629
subplot=False,
596630
artist=cax)
597-
598-
iflocation=='right':
599-
# arrange to right of the gridpec sibbling
600-
layoutbox.hstack([gslb,lb],padding=pad*gslb.width,
601-
strength='strong')
602-
else:
603-
layoutbox.hstack([lb,gslb],padding=pad*gslb.width)
631+
foraxinparents:
632+
iflocation=='right':
633+
order= [ax._layoutbox,lb]
634+
else:
635+
order= [lb,ax._layoutbox]
636+
layoutbox.hstack(order,padding=pad*gslb.width,
637+
strength='strong')
604638
# constrain the height and center...
605639
# This isn't quite right. We'd like the colorbar
606640
# pos to line up w/ the axes poss, not the size of the
607641
# gs.
608-
maxrow=-100000
609-
minrow=1000000
610-
maxax=None
611-
minax=None
612642

613-
foraxinparents:
614-
subspec=ax.get_subplotspec()
615-
nrows,ncols=subspec.get_gridspec().get_geometry()
616-
fornumin [subspec.num1,subspec.num2]:
617-
rownum1,colnum1=divmod(subspec.num1,ncols)
618-
ifrownum1>maxrow:
619-
maxrow=rownum1
620-
maxax=ax
621-
ifrownum1<minrow:
622-
minrow=rownum1
623-
minax=ax
624-
# invert the order so these are bottom to top:
625-
maxposlb=minax._poslayoutbox
626-
minposlb=maxax._poslayoutbox
643+
# Horizontal Layout: need to check all the axes in this gridspec
644+
forchingslb.children:
645+
subspec=ch.artist
646+
nrows,ncols,row_start,row_stop,col_start,col_stop= \
647+
subspec.get_rows_columns()
648+
iflocation=='right':
649+
ifcol_stop<=maxcol:
650+
order= [subspec._layoutbox,lb]
651+
# arrange to right of the parents
652+
ifcol_start>maxcol:
653+
order= [lb,subspec._layoutbox]
654+
eliflocation=='left':
655+
ifcol_start>=mincol:
656+
order= [lb,subspec._layoutbox]
657+
ifcol_stop<mincol:
658+
order= [subspec._layoutbox,lb]
659+
layoutbox.hstack(order,padding=pad*gslb.width,
660+
strength='strong')
661+
662+
# Vertical layout:
663+
maxposlb=minax_row._poslayoutbox
664+
minposlb=maxax_row._poslayoutbox
627665
# now we want the height of the colorbar pos to be
628-
# set by the top and bottom ofthese poss
629-
# bottomtop
666+
# set by the top and bottom ofthe min/max axes...
667+
# bottom top
630668
# b t
631669
# h = (top-bottom)*shrink
632670
# b = bottom + (top-bottom - h) / 2.
@@ -650,29 +688,35 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
650688
subplot=False,
651689
artist=cax)
652690

653-
iflocation=='bottom':
654-
layoutbox.vstack([gslb,lb],padding=pad*gslb.width)
655-
else:
656-
layoutbox.vstack([lb,gslb],padding=pad*gslb.width)
657-
658-
maxcol=-100000
659-
mincol=1000000
660-
maxax=None
661-
minax=None
662-
663691
foraxinparents:
664-
subspec=ax.get_subplotspec()
665-
nrows,ncols=subspec.get_gridspec().get_geometry()
666-
fornumin [subspec.num1,subspec.num2]:
667-
rownum1,colnum1=divmod(subspec.num1,ncols)
668-
ifcolnum1>maxcol:
669-
maxcol=colnum1
670-
maxax=ax
671-
ifrownum1<mincol:
672-
mincol=colnum1
673-
minax=ax
674-
maxposlb=maxax._poslayoutbox
675-
minposlb=minax._poslayoutbox
692+
iflocation=='bottom':
693+
order= [ax._layoutbox,lb]
694+
else:
695+
order= [lb,ax._layoutbox]
696+
layoutbox.vstack(order,padding=pad*gslb.width,
697+
strength='strong')
698+
699+
# Vertical Layout: need to check all the axes in this gridspec
700+
forchingslb.children:
701+
subspec=ch.artist
702+
nrows,ncols,row_start,row_stop,col_start,col_stop= \
703+
subspec.get_rows_columns()
704+
iflocation=='bottom':
705+
ifrow_stop<=minrow:
706+
order= [subspec._layoutbox,lb]
707+
ifrow_start>maxrow:
708+
order= [lb,subspec._layoutbox]
709+
eliflocation=='top':
710+
ifrow_stop<minrow:
711+
order= [subspec._layoutbox,lb]
712+
ifrow_start>=maxrow:
713+
order= [lb,subspec._layoutbox]
714+
layoutbox.vstack(order,padding=pad*gslb.width,
715+
strength='strong')
716+
717+
# Do horizontal layout...
718+
maxposlb=maxax_col._poslayoutbox
719+
minposlb=minax_col._poslayoutbox
676720
lbpos.constrain_width((maxposlb.right-minposlb.left)*
677721
shrink)
678722
lbpos.constrain_left(

‎lib/matplotlib/colorbar.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,7 @@ def make_axes(parents, location=None, orientation=None, fraction=0.15,
12431243
Returns (cax, kw), the child axes and the reduced kw dictionary to be
12441244
passed when creating the colorbar instance.
12451245
'''
1246+
12461247
locations= ["left","right","top","bottom"]
12471248
iforientationisnotNoneandlocationisnotNone:
12481249
raiseTypeError('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
foriinrange(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+
deftest_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+
foraxinaxs.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+
#containinga `~.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 sharethesame gridspec.
136+
# ``ax`` argument of ``colorbar``, constrained_layout will take space from
137+
# thespecified axes.
138138

139139
fig,axs=plt.subplots(2,2,figsize=(4,4),constrained_layout=True)
140140
foraxinaxs.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+
foraxinaxs.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
@@ -458,6 +469,21 @@ def docomplicated(suptitle=None):
458469
ax2=fig.add_axes(bb_ax2)
459470

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

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp