Skip to content

FIX: long titles x/ylabel layout #17222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2020

Conversation

jklymak
Copy link
Member

@jklymak jklymak commented Apr 23, 2020

PR Summary

Closes #8201

Previously, really long titles and axes labels would kill both tight_layout and constrained_layout because they vainly would try and reduce the size of the axes to somehow make the long title "fit". However, this cannot work, so instead just ignore the dimensions these can get too long in.

This leads to a few changes, but I still think is better than squishing the axes.

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2, constrained_layout=True)
text = 'My Really long egregious, silly title that should have a carriage return'
for ax in axs:
    ax.set_title(text)

plt.show()

Before:

testLongTitle.py:12: UserWarning: constrained_layout not applied.  At least one axes collapsed to zero width or height.

(tight layout had a similar check and was not applied)

After

Now the user gets the layout manager, and the title is allowed to overflow.
testNew

The only case where maybe this is dubious is just for constrained layout

import matplotlib.pyplot as plt
fig, axs = plt.subplots(1, 2, constrained_layout=True)
text = 'My somewhat long title that overflows a bit'
for ax in [axs[0]]:
    ax.set_title(text)
plt.show()

Old:

Moved the undecorated axes over:

testOld

New

Just lets it look bad:

testNew

I think the latter is actually better because it tells the user that they really should fix their title, whereas before they got a mysterious message about the axes collapsing and the layout not being applied.

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@jklymak jklymak added the topic: geometry manager LayoutEngine, Constrained layout, Tight layout label Apr 23, 2020
@jklymak jklymak added this to the v3.4.0 milestone Apr 23, 2020
@jklymak jklymak force-pushed the fix-long-titles-layout branch 3 times, most recently from 290c255 to c50f062 Compare April 23, 2020 18:27
@jklymak jklymak modified the milestones: v3.4.0, v3.3.0 Apr 23, 2020
@jklymak jklymak marked this pull request as ready for review April 23, 2020 19:33
@jklymak jklymak force-pushed the fix-long-titles-layout branch from c50f062 to bf62cc0 Compare April 23, 2020 19:35
bt = title.get_window_extent(renderer)
if for_layout_only and bt.width > 0:
bt.x0 = (bt.x0 + bt.x1) / 2 - 0.001
bt.x1 = bt.x0 + 0.002
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got why you do this (get a nonzero but small width bbox so that the thing doesn't get cancelled out, centered at the right place) but it's a bit... mysterious and could perhaps use a comment. Also, I think instead of picking an arbitraryish epsilon=0.001 here (and a different one for axis labels) you can use e.g. 0.5, as texts can't be less than one pixel wide anyways.

bounding box using the rules above. `.axis.Axis.get_tightbbox` gets an
``ignore_label`` keyword argument, which is *None* by default, but which can
also be 'x' or 'y'. These API changes are public, but are meant to be
mostly used internally.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could make the kwargs _for_layout_only/_ignore_label with a leading underscore, depending on how private you want to make them...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, thought about it, but its possible someone else would like the functionality?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine if you want to declare this public API, I'm just not a fan of "mostly private, but not really."

If ``ignore_label`` is 'x', then the width of the label is collapsed
to near zero. If 'y', then the height is collapsed to near zero. This
is for tight/constrained_layout to be able to ignore too-long labels
when doing their layout.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps instead use the same kwarg as for Axes.get_tightbbox (to simplify documentation), i.e. for_layout_only, and then lookup self.axis_name to know which direction to ignore?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the docs are pretty sparse for axis.get_tightlayout and I wasn't sure about introspection on the axis name as a very robust way to do this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We look up rcparams based on axis_name, so that should be safe.

... by the way, how does that interact with polar plots?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about polar plots.... Does tight_layout work with those at all?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks so (e.g. try subplots(2, 2, subplot_kw=dict(projection="polar")); title("foo"); tight_layout()).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, well that code runs fine with this PR....

@tacaswell
Copy link
Member

I am 👍 on this in principle.

How terrible would it be to put the calls in try...except TypeError block to warn and fall back to the old signature if we get any Axes / Axis subclasses that do funny things here (thinking of cartopy / wcxaxes).

attn @astrofrog @QuLogic

@jklymak
Copy link
Member Author

jklymak commented Apr 24, 2020

You meaning someone redefined get_tightbbox and were strictly checking arguments? It’s a pretty low level function to be strict with.

@QuLogic
Copy link
Member

QuLogic commented Apr 24, 2020

There's only one call to label.get_tightbbox(renderer) in Cartopy, and no subclassed definitions of get_tightbbox.

@QuLogic
Copy link
Member

QuLogic commented Apr 27, 2020

Actually, there's one PR SciTools/cartopy#1355 that would add a get_tightbbox, but it's currently out-of-date.

@jklymak
Copy link
Member Author

jklymak commented Apr 28, 2020

Right but the problem is if they check for the argument. If they don’t it will just be ignored and they won’t get the benefit of this PR

@jklymak jklymak force-pushed the fix-long-titles-layout branch 2 times, most recently from 36db363 to acbd1c3 Compare April 28, 2020 15:23
bb_yaxis = self.yaxis.get_tightbbox(renderer, ignore_label=igl)
except TypeError as e:
# in case downstream library has redefined axis:
bb_xaxis = self.yaxis.get_tightbbox(renderer)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to warn so down-stream knows they can make this signature change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that we should warn. But now I'm wondering if passing kwords around makes sense....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative is we provide new methods for these. i.e. get_tightbbox_forlayout that does the "right" thing. A second alternative is that the layout engines back out the correct info themselves, but that seems hard.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the way this is done now is probably still the best, but other solutions welcome.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you may need something similar to #12635?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't warn in #12635 so we should probably stay consistent about that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since there is nothing the end user can do about the warning, and downstream libraries don't need this feature not warning is right. It may be hard to advertise for downstream libraries, but its not a super crucial feature in my opinion, just a nicety.

@jklymak jklymak force-pushed the fix-long-titles-layout branch from f5f7580 to 474a90c Compare April 30, 2020 20:31
@QuLogic QuLogic merged commit 67a987e into matplotlib:master Apr 30, 2020
@jklymak jklymak deleted the fix-long-titles-layout branch March 10, 2024 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: geometry manager LayoutEngine, Constrained layout, Tight layout
Projects
None yet
Development

Successfully merging this pull request may close these issues.

tight_layout is incorrect for certain title sizes
4 participants