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

Appearance settings

Commit 67a987e

Browse filesBrowse files
authored
Merge pull request #17222 from jklymak/fix-long-titles-layout
FIX: long titles x/ylabel layout
2 parents 1567063 + 474a90c commit 67a987e
Copy full SHA for 67a987e

File tree

Expand file treeCollapse file tree

8 files changed

+82
-23
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+82
-23
lines changed

‎doc/api/api_changes_3.3/behaviour.rst

Copy file name to clipboardExpand all lines: doc/api/api_changes_3.3/behaviour.rst
+15-1Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,6 @@ no longer accept the unsupported ``'best'`` location. Previously, invalid Axes
201201
locations would use ``'best'`` and invalid Figure locations would used ``'upper
202202
right'``.
203203

204-
205204
Passing Line2D's *drawstyle* together with *linestyle* is removed
206205
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
207206

@@ -214,3 +213,18 @@ Upper case color strings
214213

215214
Support for passing single-letter colors (one of "rgbcmykw") as UPPERCASE
216215
characters is removed; these colors are now case-sensitive (lowercase).
216+
217+
tight/constrained_layout no longer worry about titles that are too wide
218+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
219+
220+
*tight_layout* and *constrained_layout* shrink axes to accommodate
221+
"decorations" on the axes. However, if an xlabel or title is too long in the
222+
x direction, making the axes smaller in the x-direction doesn't help. The
223+
behavior of both has been changed to ignore the width of the title and
224+
xlabel and the height of the ylabel in the layout logic.
225+
226+
This also means there is a new keyword argument for `.axes.Axes.get_tightbbox`:
227+
``for_layout_only``, which defaults to *False*, but if *True* returns a
228+
bounding box using the rules above. `.axis.Axis.get_tightbbox` gets an
229+
``ignore_label`` keyword argument, which is *None* by default, but which can
230+
also be 'x' or 'y'.

‎lib/matplotlib/_constrained_layout.py

Copy file name to clipboardExpand all lines: lib/matplotlib/_constrained_layout.py
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,11 @@ def _make_layout_margins(ax, renderer, h_pad, w_pad):
258258
fig = ax.figure
259259
invTransFig = fig.transFigure.inverted().transform_bbox
260260
pos = ax.get_position(original=True)
261-
tightbbox = ax.get_tightbbox(renderer=renderer)
261+
try:
262+
tightbbox = ax.get_tightbbox(renderer=renderer, for_layout_only=True)
263+
except TypeError:
264+
tightbbox = ax.get_tightbbox(renderer=renderer)
265+
262266
if tightbbox is None:
263267
bbox = pos
264268
else:

‎lib/matplotlib/axes/_base.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_base.py
+29-7Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4080,11 +4080,15 @@ def get_default_bbox_extra_artists(self):
40804080
for _axis in self._get_axis_list():
40814081
artists.remove(_axis)
40824082

4083+
artists.remove(self.title)
4084+
artists.remove(self._left_title)
4085+
artists.remove(self._right_title)
4086+
40834087
return [artist for artist in artists
40844088
if (artist.get_visible() and artist.get_in_layout())]
40854089

40864090
def get_tightbbox(self, renderer, call_axes_locator=True,
4087-
bbox_extra_artists=None):
4091+
bbox_extra_artists=None, *, for_layout_only=False):
40884092
"""
40894093
Return the tight bounding box of the axes, including axis and their
40904094
decorators (xlabel, title, etc).
@@ -4110,6 +4114,10 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
41104114
caller is only interested in the relative size of the tightbbox
41114115
compared to the axes bbox.
41124116
4117+
for_layout_only : default: False
4118+
The bounding box will *not* include the x-extent of the title and
4119+
the xlabel, or the y-extent of the ylabel.
4120+
41134121
Returns
41144122
-------
41154123
`.BboxBase`
@@ -4135,22 +4143,37 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
41354143
self.apply_aspect()
41364144

41374145
if self.axison:
4138-
bb_xaxis = self.xaxis.get_tightbbox(renderer)
4146+
igl = 'x' if for_layout_only else None
4147+
try:
4148+
bb_xaxis = self.xaxis.get_tightbbox(renderer, ignore_label=igl)
4149+
except TypeError:
4150+
# in case downstream library has redefined axis:
4151+
bb_xaxis = self.xaxis.get_tightbbox(renderer)
41394152
if bb_xaxis:
41404153
bb.append(bb_xaxis)
41414154

4142-
bb_yaxis = self.yaxis.get_tightbbox(renderer)
4155+
igl = 'y' if for_layout_only else None
4156+
try:
4157+
bb_yaxis = self.yaxis.get_tightbbox(renderer, ignore_label=igl)
4158+
except TypeError:
4159+
# in case downstream library has redefined axis:
4160+
bb_xaxis = self.yaxis.get_tightbbox(renderer)
41434161
if bb_yaxis:
41444162
bb.append(bb_yaxis)
4145-
41464163
self._update_title_position(renderer)
4147-
41484164
axbbox = self.get_window_extent(renderer)
41494165
bb.append(axbbox)
41504166

41514167
for title in [self.title, self._left_title, self._right_title]:
41524168
if title.get_visible():
4153-
bb.append(title.get_window_extent(renderer))
4169+
bt = title.get_window_extent(renderer)
4170+
if for_layout_only and bt.width > 0:
4171+
# make the title bbox 1 pixel wide so its width
4172+
# is not accounted for in bbox calculations in
4173+
# tight/constrained_layout
4174+
bt.x0 = (bt.x0 + bt.x1) / 2 - 0.5
4175+
bt.x1 = bt.x0 + 1.0
4176+
bb.append(bt)
41544177

41554178
bbox_artists = bbox_extra_artists
41564179
if bbox_artists is None:
@@ -4173,7 +4196,6 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
41734196
and 0 < bbox.width < np.inf
41744197
and 0 < bbox.height < np.inf):
41754198
bb.append(bbox)
4176-
41774199
return mtransforms.Bbox.union(
41784200
[b for b in bb if b.width != 0 or b.height != 0])
41794201

‎lib/matplotlib/axis.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axis.py
+20-2Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,10 +1079,15 @@ def _get_tick_bboxes(self, ticks, renderer):
10791079
[tick.label2.get_window_extent(renderer)
10801080
for tick in ticks if tick.label2.get_visible()])
10811081

1082-
def get_tightbbox(self, renderer):
1082+
def get_tightbbox(self, renderer, *, ignore_label=None):
10831083
"""
10841084
Return a bounding box that encloses the axis. It only accounts
10851085
tick labels, axis label, and offsetText.
1086+
1087+
If ``ignore_label`` is 'x', then the width of the label is collapsed
1088+
to near zero. If 'y', then the height is collapsed to near zero. This
1089+
is for tight/constrained_layout to be able to ignore too-long labels
1090+
when doing their layout.
10861091
"""
10871092
if not self.get_visible():
10881093
return
@@ -1100,11 +1105,24 @@ def get_tightbbox(self, renderer):
11001105

11011106
bboxes = [
11021107
*(a.get_window_extent(renderer)
1103-
for a in [self.label, self.offsetText]
1108+
for a in [self.offsetText]
11041109
if a.get_visible()),
11051110
*ticklabelBoxes,
11061111
*ticklabelBoxes2,
11071112
]
1113+
# take care of label
1114+
if self.label.get_visible():
1115+
bb = self.label.get_window_extent(renderer)
1116+
# for constrained/tight_layout, we want to ignore the label's
1117+
# width because the adjustments they make can't be improved.
1118+
# this code collapses the relevant direction
1119+
if ignore_label == 'x' and bb.width > 0:
1120+
bb.x0 = (bb.x0 + bb.x1) / 2 - 0.5
1121+
bb.x1 = bb.x0 + 1.0
1122+
elif ignore_label == 'y' and bb.height > 0:
1123+
bb.y0 = (bb.y0 + bb.y1) / 2 - 0.5
1124+
bb.y1 = bb.y0 + 1.0
1125+
bboxes.append(bb)
11081126
bboxes = [b for b in bboxes
11091127
if 0 < b.width < np.inf and 0 < b.height < np.inf]
11101128
if bboxes:
Loading

‎lib/matplotlib/tests/test_constrainedlayout.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_constrainedlayout.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def test_constrained_layout10():
176176
@image_comparison(['constrained_layout11.png'])
177177
def test_constrained_layout11():
178178
"""Test for multiple nested gridspecs"""
179-
fig = plt.figure(constrained_layout=True, figsize=(10, 3))
179+
fig = plt.figure(constrained_layout=True, figsize=(13, 3))
180180
gs0 = gridspec.GridSpec(1, 2, figure=fig)
181181
gsl = gridspec.GridSpecFromSubplotSpec(1, 2, gs0[0])
182182
gsl0 = gridspec.GridSpecFromSubplotSpec(2, 2, gsl[1])

‎lib/matplotlib/tests/test_tightlayout.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_tightlayout.py
+3-9Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -258,29 +258,23 @@ def test_empty_layout():
258258

259259
@pytest.mark.parametrize("label", ["xlabel", "ylabel"])
260260
def test_verybig_decorators(label):
261-
"""Test that warning emitted when xlabel/ylabel too big."""
261+
"""Test that no warning emitted when xlabel/ylabel too big."""
262262
fig, ax = plt.subplots(figsize=(3, 2))
263263
ax.set(**{label: 'a' * 100})
264-
with pytest.warns(UserWarning):
265-
fig.tight_layout()
266264

267265

268266
def test_big_decorators_horizontal():
269-
"""Test that warning emitted when xlabel too big."""
267+
"""Test that doesn't warn when xlabel too big."""
270268
fig, axs = plt.subplots(1, 2, figsize=(3, 2))
271269
axs[0].set_xlabel('a' * 30)
272270
axs[1].set_xlabel('b' * 30)
273-
with pytest.warns(UserWarning):
274-
fig.tight_layout()
275271

276272

277273
def test_big_decorators_vertical():
278-
"""Test that warning emitted when xlabel too big."""
274+
"""Test that doesn't warn when ylabel too big."""
279275
fig, axs = plt.subplots(2, 1, figsize=(3, 2))
280276
axs[0].set_ylabel('a' * 20)
281277
axs[1].set_ylabel('b' * 20)
282-
with pytest.warns(UserWarning):
283-
fig.tight_layout()
284278

285279

286280
def test_badsubplotgrid():

‎lib/matplotlib/tight_layout.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tight_layout.py
+9-2Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,15 @@ def auto_adjust_subplotpars(
7777
if all(not ax.get_visible() for ax in subplots):
7878
continue
7979

80-
tight_bbox_raw = Bbox.union([
81-
ax.get_tightbbox(renderer) for ax in subplots if ax.get_visible()])
80+
bb = []
81+
for ax in subplots:
82+
if ax.get_visible():
83+
try:
84+
bb += [ax.get_tightbbox(renderer, for_layout_only=True)]
85+
except TypeError:
86+
bb += [ax.get_tightbbox(renderer)]
87+
88+
tight_bbox_raw = Bbox.union(bb)
8289
tight_bbox = TransformedBbox(tight_bbox_raw,
8390
fig.transFigure.inverted())
8491

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.