-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
FIX: fix bbox_inches=tight and constrained layout bad interaction #19342
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not claiming I've fully dug into the code, but the structure looks a bit odd:
- There's a lot of distance between saving
cl_state
and deactivating the layout. - The save/restore always happens, but deactivating only happens
if bbox_inches == "tight"
Can't this be pulled closer together? Ideally, if we had a with figure._constrained_layout_deactivated()
context manager could this be used here?
The issue is that bbox_inches=tight makes an extra draw, and then changes the size of the figure. You only want to turn CL off after that extra draw. Otherwise you want to leave it as the user set it because the |
I guess the other way of expressing this is that I think the original issue is that the way we do the bbox_inches='tight' is not terribly intuitive. In an ideal world, we would just make the figure normally, and crop it afterwards in a backend-appropriate way. Instead, we make a smaller canvas, and temporarily transform all the artists a dx and dy, and then draw the figure. CL balks at this because it then tries to resize on the smaller trimmed figure using margins etc that were based on the bigger figure. Its a case of two types of semi-fragile magics not getting along. |
Though thinking about it some more, I forgot to check if |
lib/matplotlib/backend_bases.py
Outdated
# we have already done CL above, so turn it off: | ||
cl_state = self.figure.get_constrained_layout() | ||
self.figure.set_constrained_layout(False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this should not be part of try
. We're not expecting it to fail.
Also, does it make sense to
cl_state = self.figure.get_constrained_layout()
if cl_state:
self.figure.set_constrained_layout(False)
[...]
if cl_state:
self.figure.set_constrained_layout(cl_state)
to prevent any overhead if constrained is not used?
Note that this can easily be refactored to a context manager now:
with self.figure._constrained_layout_deactivated():
try:
[...]
finally:
[...]
return result
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, the set can be outside the try
.
Actually, now that you write this, I think that's probably doable (not in the 3.4 time frame of course)... I'll keep that in the back of my mind for the future. |
For raster backends, it is pretty trivial - just crop the canvas before write. For the vector backends, I'm not sure what the incantation needs to be to do that robustly.... |
For svg we can probably handle with https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox. |
Yeah postscript (and I assume friends) has a translate command. Something like this would be so much more straightforward than the song and dance we do now. But as you say, not for 3.4. |
I thought about the context manager, but I am not sure if its worth creating a context manager in I am happy to include the if-statements if you like, but I think |
I'm slightly in favor of a context manager because it makes the code more readable, even if the context manager is only used once. But I'm also ok without. That's why I've already approved. |
@anntzer can you confirm if this fixes the issue for you? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works for me. Thanks for the quick fix :)
I think the context manager, if any, should be |
PR Summary
Closes #19339
Old
Bad interaction between width and current size of the axes:
New:
Suppress constrained layout after the first draw in the bbox_inches=tight logic
Sorry, this obviously needs a new image test unless someone knows how to mock up
bbox_inches='tight'
behaviour without actually saving the figure.PR Checklist
pytest
passes).flake8
on changed files to check).flake8-docstrings
and runflake8 --docstring-convention=all
).doc/users/next_whats_new/
(follow instructions in README.rst there).doc/api/next_api_changes/
(follow instructions in README.rst there).