Skip to content

Figure resize when saving a plot #8736

Description

@rayosborn

Bug report

Bug summary

The print_figure function in the FigureCanvasBase class records the current figure's dpi, sets it to the requested dpi for the saved figure, and then resets it to the original. Setting the dpi invokes a property function, _set_dpi that calls the figure's set_size_inches method, which then triggers a resize of the figure manager.

This resize is not evident when I use the standard Pyplot commands within a Qt console, presumably because the plot size doesn't change. However, I have embedded the Matplotlib figure in a PyQt GUI where I don't have the toolbar in the same widget, and it causes a noticeable size change for a fraction of a second before the second dpi reset restores the old size.

Possible solution

I would like to suggest that the FigureCanvasBase print_figure function should not invoke the dpi property, but set and then reset the figure's hidden variable, _dpi, which will not trigger the redundant canvas resize. I can submit a pull request, but I wanted to check if there would be side effects.

Matplotlib version

  • Matplotlib Version: 2.0.2
  • Operating System: Mac OS 10.12.5
  • Python Version: 2.7.11
  • PyQt 4.8.7

Activity

  1. tacaswell commented on Jun 9, 2017

    @tacaswell
    Member

    This is a side-effect of the work to support high-dpi screens and the need to temporarily double the dpi.

    What is the dpi on the screen vs the dpi you are saving at? Is it a big change (factor of 2) or a small one? I suspect that this may actually be a bug in the figure size not round-tripping properly rather than in the dpi handling per-say. I am also not sure off the top of my head what is forcing the re-draw in the GUI. Are you using pyplot + embedding?

    If we are going to go down the route of disabling the auto-redraw can it be done as a context manager (this is one of many things where we should be using context managers, but the code was written before context managers existed 😜 ).

  2. rayosborn commented on Jun 9, 2017

    @rayosborn
    Author

    I would say that it is an approximately 10% reduction in width during the temporary resize. The requested dpi of the PNG file I am saving is 100, and a little googling shows that the Thunderbolt display I am using has a dpi of 109, so that could be consistent. It's puzzling that the value of _dpi was already 100 before the resize, so another possible fix is to make sure that _set_dpi only triggers a resize if the new value is different (or is that something that context managers handle?).

    The GUI subclasses FigureManagerQT, so it is using the save_figure function in backend_qt5.py. Matplotlib 2 must have added the _status_and_tool_height parameter, which I have set to 0, since my toolbar is in a different widget. It was while investigating why the parameter was needed that I encountered this issue.

    If there is a long-term plan that will render this issue moot, then it can probably wait. It's just disconcerting seeing the window suddenly change size, and it took me a while to persuade myself that it reverted back to the same size.

  3. added this to the milestone on Jun 12, 2017
  4. tacaswell commented on Jun 12, 2017

    @tacaswell
    Member

    Probably related to roughly related to #8717

  5. modified the milestones: , on Jun 12, 2017
  6. rayosborn commented on Jun 13, 2017

    @rayosborn
    Author

    I've finally realized what the problem is. When setting the dpi figure property, it restores the figure manager window to the canvas height and width, not the original window's height and width. There is a fudge in the FigureManagerQT resize function that adds the toolbar height to the canvas height, which works within Matplotlib, but I would argue that it screws up any attempt to embed a Matplotlib canvas in another GUI as I am doing. It would be much cleaner to confine canvas resizes to the canvas widget, and let PyQt handle the window resizes.

  7. rayosborn commented on Jun 13, 2017

    @rayosborn
    Author

    As a follow-up, I had already subclassed FigureManagerQT for other reasons. If I add the following resize function to the class, then my problems disappear, i.e., there is no discernible resize of the plotting window, temporary or permanent. I don't think it is related to screen resolutions.

    def resize(self, width, height):
        extra_width = self.window.width() - self.canvas.width()
        extra_height = self.window.height() - self.canvas.height()
        self.window.resize(width+extra_width, height+extra_height)
    

    You could presumably do the same thing in FigureManagerQT, since canvas is still its own widget, embedded in the main window.

  8. tacaswell commented on Jun 13, 2017

    @tacaswell
    Member

    that is a very experimentalist approach, I like it 👍

  9. tacaswell commented on Jun 13, 2017

    @tacaswell
    Member

    exact work:

  10. added a commit that references this issue on Aug 31, 2017
    28b37c1
  11. modified the milestones: , on Sep 24, 2017
  12. added
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    on Oct 16, 2017
  13. modified the milestones: , v2.2.0 on Feb 12, 2018
  14. Datseris commented on Apr 6, 2018

    @Datseris

    Hello! Can you please tell me how I can solve this issue? I have the exact same problem: I am saving a figure over and over again, so that I can animate it afterwards but the figure keeps shriniking in size after successive saves...

    I have set a custom dpi, which I though was the "fix" mentioned in #8805, but it did not make any difference.

  15. tacaswell commented on Apr 6, 2018

    @tacaswell
    Member

    What version of Matplotlib are you using? This should have been fixed in the 2.2 series.

  16. Datseris commented on Apr 6, 2018

    @Datseris

    Yes you are right, sorry for the spam. Turns out I wasn't up to date with the latest version. Now everything is working properly!

  17. earnestt1234 commented on Jun 4, 2020

    @earnestt1234

    I'm having the same issue as above, particularly like what @Datseris described.

    I have a tkinter GUI for doing some plotting which has a tab displaying a single Figure with Axes that get cleared and updated when new plots are created/reselected. When saving one file, (using savefig), there is no issue, but when saving multiple plots iteratively (i.e. save the figure, clear axes, update, repeat...), the graph shrinks (both in the GUI and in the saved images). Here's a gif displaying the issue. The Figure is rendered at 150 DPI in the application, but I am trying to save at 300 DPI (specified when calling savefig, if I leave out the dpi argument I don't have the issue).

    I'm using matplotlib v 3.2.1. Any ideas for a fix or workaround? I don't quite follow all of the thread above... but let me know if I can provide any other information. Thanks!

  18. earnestt1234 commented on Jun 5, 2020

    @earnestt1234

    I added lines to explicitly update the canvas and the application every loop, and that seemed to fix the issue. NVM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUI: Qt🌱 Good first issueOpen a pull request against these issues if there are no active ones!

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions