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

Commitdcff41f

Browse files
Fix colorbar alignment with suptitle in compressed layout mode (#30766)
*#30472 fix issue with colorbar+suptext in compressed mode* fix horizontal colorbar---------Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
1 parent7a7a388 commitdcff41f

File tree

4 files changed

+104
-5
lines changed

4 files changed

+104
-5
lines changed

‎lib/matplotlib/_constrained_layout.py‎

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ def do_constrained_layout(fig, h_pad, w_pad,
137137
layoutgrids[fig].update_variables()
138138
ifcheck_no_collapsed_axes(layoutgrids,fig):
139139
reposition_axes(layoutgrids,fig,renderer,h_pad=h_pad,
140-
w_pad=w_pad,hspace=hspace,wspace=wspace)
140+
w_pad=w_pad,hspace=hspace,wspace=wspace,
141+
compress=True)
141142
else:
142143
_api.warn_external(warn_collapsed)
143144

@@ -651,7 +652,7 @@ def get_pos_and_bbox(ax, renderer):
651652

652653

653654
defreposition_axes(layoutgrids,fig,renderer,*,
654-
w_pad=0,h_pad=0,hspace=0,wspace=0):
655+
w_pad=0,h_pad=0,hspace=0,wspace=0,compress=False):
655656
"""
656657
Reposition all the Axes based on the new inner bounding box.
657658
"""
@@ -662,7 +663,7 @@ def reposition_axes(layoutgrids, fig, renderer, *,
662663
bbox=bbox.transformed(trans_fig_to_subfig))
663664
reposition_axes(layoutgrids,sfig,renderer,
664665
w_pad=w_pad,h_pad=h_pad,
665-
wspace=wspace,hspace=hspace)
666+
wspace=wspace,hspace=hspace,compress=compress)
666667

667668
foraxinfig._localaxes:
668669
ifax.get_subplotspec()isNoneornotax.get_in_layout():
@@ -689,10 +690,10 @@ def reposition_axes(layoutgrids, fig, renderer, *,
689690
fornn,cbaxinenumerate(ax._colorbars[::-1]):
690691
ifax==cbax._colorbar_info['parents'][0]:
691692
reposition_colorbar(layoutgrids,cbax,renderer,
692-
offset=offset)
693+
offset=offset,compress=compress)
693694

694695

695-
defreposition_colorbar(layoutgrids,cbax,renderer,*,offset=None):
696+
defreposition_colorbar(layoutgrids,cbax,renderer,*,offset=None,compress=False):
696697
"""
697698
Place the colorbar in its new place.
698699
@@ -706,6 +707,8 @@ def reposition_colorbar(layoutgrids, cbax, renderer, *, offset=None):
706707
offset : array-like
707708
Offset the colorbar needs to be pushed to in order to
708709
account for multiple colorbars.
710+
compress : bool
711+
Whether we're in compressed layout mode.
709712
"""
710713

711714
parents=cbax._colorbar_info['parents']
@@ -724,6 +727,31 @@ def reposition_colorbar(layoutgrids, cbax, renderer, *, offset=None):
724727
aspect=cbax._colorbar_info['aspect']
725728
shrink=cbax._colorbar_info['shrink']
726729

730+
# For colorbars with a single parent in compressed layout,
731+
# use the actual visual size of the parent axis after apply_aspect()
732+
# has been called. This ensures colorbars align with their parent axes.
733+
# This fix is specific to single-parent colorbars where alignment is critical.
734+
ifcompressandlen(parents)==1:
735+
frommatplotlib.transformsimportBbox
736+
# Get the actual parent position after apply_aspect()
737+
parent_ax=parents[0]
738+
actual_pos=parent_ax.get_position(original=False)
739+
# Transform to figure coordinates
740+
actual_pos_fig=actual_pos.transformed(fig.transSubfigure-fig.transFigure)
741+
742+
iflocationin ('left','right'):
743+
# For vertical colorbars, use the actual parent bbox height
744+
# for colorbar sizing
745+
# Keep the pb x-coordinates but use actual y-coordinates
746+
pb=Bbox.from_extents(pb.x0,actual_pos_fig.y0,
747+
pb.x1,actual_pos_fig.y1)
748+
eliflocationin ('top','bottom'):
749+
# For horizontal colorbars, use the actual parent bbox width
750+
# for colorbar sizing
751+
# Keep the pb y-coordinates but use actual x-coordinates
752+
pb=Bbox.from_extents(actual_pos_fig.x0,pb.y0,
753+
actual_pos_fig.x1,pb.y1)
754+
727755
cbpos,cbbbox=get_pos_and_bbox(cbax,renderer)
728756

729757
# Colorbar gets put at extreme edge of outer bbox of the subplotspec
3.45 KB
Loading
4.9 KB
Loading

‎lib/matplotlib/tests/test_constrainedlayout.py‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,77 @@ def test_compressed_suptitle():
688688
asserttitle.get_position()[1]==0.98
689689

690690

691+
@image_comparison(['test_compressed_suptitle_colorbar.png'],style='mpl20')
692+
deftest_compressed_suptitle_colorbar():
693+
"""Test that colorbars align with axes in compressed layout with suptitle."""
694+
arr=np.arange(100).reshape((10,10))
695+
fig,axs=plt.subplots(ncols=2,figsize=(4,2),layout='compressed')
696+
697+
im0=axs[0].imshow(arr)
698+
im1=axs[1].imshow(arr)
699+
700+
cb0=plt.colorbar(im0,ax=axs[0])
701+
cb1=plt.colorbar(im1,ax=axs[1])
702+
703+
fig.suptitle('Title')
704+
705+
# Verify colorbar heights match axes heights
706+
# After layout, colorbar should have same height as parent axes
707+
fig.canvas.draw()
708+
709+
forax,cbinzip(axs, [cb0,cb1]):
710+
ax_pos=ax.get_position()
711+
cb_pos=cb.ax.get_position()
712+
713+
# Check that colorbar height matches axes height (within tolerance)
714+
# Note: We check the actual rendered positions, not the bbox
715+
assertabs(cb_pos.height-ax_pos.height)<0.01, \
716+
f"Colorbar height{cb_pos.height} doesn't match axes height{ax_pos.height}"
717+
718+
# Also verify vertical alignment (y0 and y1 should match)
719+
assertabs(cb_pos.y0-ax_pos.y0)<0.01, \
720+
f"Colorbar y0{cb_pos.y0} doesn't match axes y0{ax_pos.y0}"
721+
assertabs(cb_pos.y1-ax_pos.y1)<0.01, \
722+
f"Colorbar y1{cb_pos.y1} doesn't match axes y1{ax_pos.y1}"
723+
724+
725+
@image_comparison(['test_compressed_supylabel_colorbar.png'],style='mpl20')
726+
deftest_compressed_supylabel_colorbar():
727+
"""
728+
Test that horizontal colorbars align with axes
729+
in compressed layout with supylabel.
730+
"""
731+
arr=np.arange(100).reshape((10,10))
732+
fig,axs=plt.subplots(nrows=2,figsize=(3,4),layout='compressed')
733+
734+
im0=axs[0].imshow(arr)
735+
im1=axs[1].imshow(arr)
736+
737+
cb0=plt.colorbar(im0,ax=axs[0],orientation='horizontal')
738+
cb1=plt.colorbar(im1,ax=axs[1],orientation='horizontal')
739+
740+
fig.supylabel('Title')
741+
742+
# Verify colorbar widths match axes widths
743+
# After layout, colorbar should have same width as parent axes
744+
fig.canvas.draw()
745+
746+
forax,cbinzip(axs, [cb0,cb1]):
747+
ax_pos=ax.get_position()
748+
cb_pos=cb.ax.get_position()
749+
750+
# Check that colorbar width matches axes width (within tolerance)
751+
# Note: We check the actual rendered positions, not the bbox
752+
assertabs(cb_pos.width-ax_pos.width)<0.01, \
753+
f"Colorbar width{cb_pos.width} doesn't match axes width{ax_pos.width}"
754+
755+
# Also verify horizontal alignment (x0 and x1 should match)
756+
assertabs(cb_pos.x0-ax_pos.x0)<0.01, \
757+
f"Colorbar x0{cb_pos.x0} doesn't match axes x0{ax_pos.x0}"
758+
assertabs(cb_pos.x1-ax_pos.x1)<0.01, \
759+
f"Colorbar x1{cb_pos.x1} doesn't match axes x1{ax_pos.x1}"
760+
761+
691762
@pytest.mark.parametrize('arg, state', [
692763
(True,True),
693764
(False,False),

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp