Skip to content

Unset the canvas manager when saving the figure. #10292

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
Jan 24, 2018

Conversation

anntzer
Copy link
Contributor

@anntzer anntzer commented Jan 22, 2018

Saving a figure may involve temporarily resizing the canvas (e.g. due to
different dpi), but we don't want that resize to be propagated to the
actual GUI widget. Ensure this by temporarily unsetting the manager as
well.

Closes #8736, #10287, and (I believe) #8852. #9131 still remains valid though I believe.
Milestoning as 2.2 only to match the above mentioned issues.

PR Summary

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 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

@anntzer anntzer added this to the v2.2 milestone Jan 22, 2018
@jklymak jklymak added the Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. label Jan 23, 2018
@jklymak
Copy link
Member

jklymak commented Jan 23, 2018

This addresses enough bugs and seems manageable to review for 2.2

@@ -2149,6 +2150,15 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,

"""
self._is_saving = True
# Remove the figure manager, if any, to avoid resizing the GUI widget.
Copy link
Member

Choose a reason for hiding this comment

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

Maybe # Remove the figure manager, currently set in some backeneds (i.e. nbagg).... I was looking at this and not understanding when self.manager ever got set. Similarly above when you instantiate it, maybe a comment on what a manager is and where one is set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a FigureManagerBase class defined in the same file, and if you look at its init you'll see it creates the manager attribute on the canvas. I think it's better to have a single doc that describes the interaction between all classes in backend_bases rather than document this in a disjoint, one-comment-at-a-time way... (but it is also true that that single doc doesn't exist yet)

Copy link
Member

Choose a reason for hiding this comment

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

I'm still confused what backend I need to use to test this works, and how. I assume I need to trigger a manual save. This works fine on Qt5Agg, but did not check if manager is set in Qt5Agg.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Set the savefig.dpi rcparam to some large number (say, three times your figure.dpi, just to be safe with hidpi stuff...) and then interactively trigger a save, while the window has a relatively small size on your desktop. Depending on yourdesktop environment (specifically, how it handles fast window resize events), you may see the window flicker in size, first to, well, three times its current size, and then back.
This resize is handled by the canvas manager. This PR disconnects the manager for the duration of the save, thus suppressing the flicker.

Copy link
Member

@jklymak jklymak Jan 24, 2018

Choose a reason for hiding this comment

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

Hmmm, I guess my machine is too fast - I don't get a flicker w/ Qt5Agg on Master or any apparent resizing. The PR doesn't make things any better or worse on Qt5Agg for me, so if it helps other folks its fine by me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you try on linux? It may well be that OSX just detects and suppresses fast resizes.
May also help if you make the plot relatively slow to draw (e.g., N=100000; scatter(rand(N), rand(N), s=rand(N))), which may make the effect more apparent.

Copy link
Member

Choose a reason for hiding this comment

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

No, I don't have a linux machine (set up for this). I guess OSX is just clever - making the plot more ornate doesn't change anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume @tacaswell checked that this works for him too?

@@ -1772,6 +1772,7 @@ def __init__(self, figure):
self._is_saving = False
figure.set_canvas(self)
self.figure = figure
self.manager = None
Copy link
Contributor

Choose a reason for hiding this comment

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

Why set None if you're checking using hasattr in print_figure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops. wanted to figure out the difference between None and not having it at all, that's leftover from some debugging. removing.

Saving a figure may involve temporarily resizing the canvas (e.g. due to
different dpi), but we don't want that resize to be propagated to the
actual GUI widget.  Ensure this by temporarily unsetting the manager as
well.
@anntzer anntzer force-pushed the managerless-savefig branch from c92e48b to a6b34d4 Compare January 23, 2018 21:09
@tacaswell
Copy link
Member

I like how direct of a solution this is.

@anntzer
Copy link
Contributor Author

anntzer commented Jan 24, 2018

You know how I value simplicity and elegance :-) </irony>
"You don't want your figure size to be managed, so you just get rid of the manager."

Copy link
Member

@jklymak jklymak left a comment

Choose a reason for hiding this comment

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

This doesn't break anything, but seems reasonable if it fixes things.

@dopplershift
Copy link
Contributor

@anntzer I just had a thought: what about making a context manager? It seems like the natural way to handle something like this. It might cause less of the details to bleed into the (already lengthy) function body. Of course, you'd have to indent a ton of lines... Just a thought, feel free to disregard.

@anntzer
Copy link
Contributor Author

anntzer commented Jan 24, 2018

I have a plan to make a setattr_cm() contextmanager so that one can do

with setattr_cm(obj, "attrname", value): ...

which is a pattern used again and again throughout the codebase. I don't think it's worth implementing a one-shot version for this PR.

@dopplershift
Copy link
Contributor

Fair--I like your general solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Figure resize when saving a plot
5 participants