Skip to content

Add scrollbars when needed to preserve specified figure size #7338

Open

Description

@Khris777

In the discussion of this post: #3139 I found the following quote by @efiring :

When the figure is rendered on the screen, the geometry does change the output of fig.get_size_inches(), and savefig uses the modified dimensions. It's not clear that there is anything we should change here.

So here's an example where I explicitely set a certain figure size using rcParams:

import pandas as pd
import matplotlib as mp
import matplotlib.pyplot as mpp
import numpy as np 

dpi = 200
mp.rcParams[u'figure.dpi'] = dpi
mp.rcParams[u'figure.figsize'] = (8.,12.)
mp.rcParams[u'savefig.dpi'] = dpi

df = pd.DataFrame({'A':np.random.rand(20),'B':np.random.rand(20)})
fig, axes = mpp.subplots(1,1)
axes.scatter(y=df.A, x=df.B)

print fig.get_size_inches()
fig.savefig("C:\\DataDir\\Output\\Test1.png")

print fig.get_size_inches()
fig.show()

print fig.get_size_inches()
fig.savefig("C:\\DataDir\\Output\\Test2.png")

print fig.get_size_inches()

mp.rcdefaults()

print statements output:

[  8.  12.]
[  8.  12.]
[ 8.    4.77]
[ 8.    4.77]

For my use-case having show() silently alter the figure size is unwanted behaviour, I'm expecting the figure size to only change when I'm explicitely changing it myself.

I can see how there are use-cases where this behaviour is expected, so I'm not saying to get rid of it, rather give the user a choice in some way, either by setting a global parameter, by telling savefig() to use rcParams[u'figure.figsize'] instead of fig.get_size_inches() when saving the figure, or however else this could be implemented nicely.

Invoking

fig.set_figwidth(mp.rcParams[u'figure.figsize'][0])
fig.set_figheight(mp.rcParams[u'figure.figsize'][1])

everytime after doing show() isn't a handy solution in any way.

Activity

  1. efiring commented on Oct 24, 2016

    @efiring
    Member

    I think the best solution would be to have scrollbars appear when the screen window is not large enough to hold the figure at the specified size and dpi. The only problem is that this requires major work on all GUI backends, and we are short of developer time and expertise for this. Maybe it would be OK to start with a subset of the backends.

  2. changed the title [-]Feature Request: Ability to keep original figure size after invoking show()[/-] [+]Add scrollbars when needed to preserve specified figure size[/+] on Jan 7, 2017
  3. efiring commented on Jan 7, 2017

    @efiring
    Member

    @Khris777 I hope you don't mind my having changed your title; I think it will make it easier to find and track the issue.

  4. added
    Difficulty: Mediumhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issues
    on Jan 7, 2017
  5. added this to the milestone on Jan 7, 2017
  6. Khris777 commented on Jan 9, 2017

    @Khris777
    Author

    @efiring
    No problem, thanks for adding this to the milestone.

  7. modified the milestones: , on Jan 18, 2018
  8. added
    API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …
    on Jan 18, 2018
  9. jklymak commented on Jan 18, 2018

    @jklymak
    Member

    This is a bit of an inconsistency in Qt5Agg, where its not giving the requested window size because it won't fit. If you do this in MacOSx it just gives you a big window thats bigger than the screen. I think this is what Qt5Agg should do as well.

    I'm moving to 3.0 because I don't see this getting fixed in next month.

  10. efiring commented on Jan 18, 2018

    @efiring
    Member

    On my machine with the macosx backend, it makes a window the size of the screen, not the specified size. And it cuts off the bottom a little bit (I think) because it is pushed down by the main menu bar.

    Qt has a QScrollArea, but I don't understand the backend code well enough to have any idea of what would be required to use it.

    The only alternative to scrollbars that I can imagine would be an option to automatically scale the dpi to make the window fit.

  11. jklymak commented on Jan 18, 2018

    @jklymak
    Member

    Really? I did:

    import matplotlib
    matplotlib.use('MacOSX')
    import matplotlib.pyplot as plt
    
    matplotlib.rcParams[u'figure.dpi'] = 200
    matplotlib.rcParams[u'figure.figsize'] = (8.,12.)
    
    fig, axes = plt.subplots(1,1)
    print(fig.get_size_inches())
    plt.show()
    print(fig.get_size_inches())
    

    and got (where my menubar is off the bottom of the screen):

    [  8.  12.]
    [  8.  12.]
    

    menubar_and_figure_1_and_github_ _gmail__2_263_messages__and_1__pythonw_testfigsize_py__python__and_2__jklymak_valdez____dropbox_mitgcm_verification__zsh_

  12. anntzer commented on Jan 18, 2018

    @anntzer
    Contributor

    I agree that using a scrollarea instead of resizing would be better. I can probably have a look at the Qt situation at some point...

  13. efiring commented on Jan 18, 2018

    @efiring
    Member

    @jklymak, I simply did "pythonw which ipython --pylab=osx" and then

    fig = plt.figure(figsize=(30, 25))

    and observed that the figure width exactly equals the screen width and I don't see the bottom of the the figure. I can wiggle the figure back and forth laterally, showing that its width does match the screen, but I couldn't move it higher to find out where the bottom is. All of this was the same if I used (20, 15), and it is on a 15" retina laptop.

  14. 9 remaining items

  15. anntzer commented on Jun 6, 2023

    @anntzer
    Contributor

    On Qt looks like the following gives a minimal working version:

    diff --git i/lib/matplotlib/backends/backend_qt.py w/lib/matplotlib/backends/backend_qt.py
    index f4b281bdd0..a16661c1c2 100644
    --- i/lib/matplotlib/backends/backend_qt.py
    +++ w/lib/matplotlib/backends/backend_qt.py
    @@ -356,8 +356,8 @@ class FigureCanvasQT(FigureCanvasBase, QtWidgets.QWidget):
             w, h = self.get_width_height()
             return QtCore.QSize(w, h)
    
    -    def minumumSizeHint(self):
    -        return QtCore.QSize(10, 10)
    +    def minimumSizeHint(self):
    +        return QtCore.QSize(500, 500)
    
         @staticmethod
         def _mpl_modifiers(modifiers=None, *, exclude=None):
    @@ -544,7 +544,10 @@ class FigureManagerQT(FigureManagerBase):
             height = cs_height + tbs_height
             self.window.resize(cs.width(), height)
    
    -        self.window.setCentralWidget(self.canvas)
    +        scrollarea = QtWidgets.QScrollArea()
    +        scrollarea.setWidget(self.canvas)
    +        scrollarea.setWidgetResizable(True)
    +        self.window.setCentralWidget(scrollarea)
    
             if mpl.is_interactive():
                 self.window.show()

    (of course you need to hide that behind a switch based on an rcparam to decide which behavior to use).

    Note that effectively there's another canvas size that needs to be defined (with all the bookkeeping associated -- here I just hardcoded the minimumSizeHint), which is the size below which scrollbars appear (the other choice would be to have an absolutely fixed canvas size and have it not increase in size if the window is made larger). @efiring Perhaps you can chime in on the exact desired behavior.

  16. efiring commented on Jun 6, 2023

    @efiring
    Member

    The goal is to always render the figure at the specified figsize in inches based on the specified dpi, regardless of the size of the window. So for any window, if it is not large enough, the scrollbars would appear. The current behavior of enlarging the window as needed, up to the max that will fit the screen, could be kept; the difference would be that when in the current version the figsize would be reduced to keep the window size within the screen, in the new version the figsize would stay the same and scrollbars would allow one to view any part of the figure.

  17. jklymak commented on Jun 6, 2023

    @jklymak
    Member

    @efiring I'm a little confused by this as the "goal". Is there a toggle for this, because folks resize their figures manually all the time by resizing the window? If this didn't have to happen, layout would be a lot easier.

  18. efiring commented on Jun 6, 2023

    @efiring
    Member

    Yes, I imagine the behavior I describe would have to be an optional alternative; preferably with an rcParam for the default, and an easy toggle. In many--perhaps most--cases the current behavior is what people will want. The use case for the alternative, with scrollbars, is for working with a figure that is to be printed or published. It ensures that the layout you get on the screen is what you will get in the pdf. Without this, one has to reduce the specified display dpi in order to get a larger figure (in terms of figsize in inches) to fit in a window that fits on the screen.

    The most extreme version of the alternative, simplifying the implementation, would be to always have the scrollbars when they are toggled on. The two modes would be "adaptive figsize" (no scrollbars, current behavior) and "fixed figsize".

  19. timhoffm commented on Jun 6, 2023

    @timhoffm
    Member

    The current behavior of enlarging the window as needed, up to the max that will fit the screen, could be kept;

    I'd prefer either completely fixed (=exact pixels) or freely scaling up/down. A mixture is rather confusing.

  20. anntzer commented on Jun 6, 2023

    @anntzer
    Contributor

    I'd prefer either completely fixed (=exact pixels) or freely scaling up/down. A mixture is rather confusing.

    In the proposed patch this can be achieved by removing the scrollarea.setWidgetResizable(True) line (and possibly additionally giving the whole window a FixedSize sizePolicy).

  21. efiring commented on Jun 6, 2023

    @efiring
    Member

    Perhaps my proposal is not yet clear; I don't understand what "mixture" you are referring to. Have 2 modes:

    1. Current behavior.
    2. figsize in inches, translated to pixels based on the dpi, does not change. The window would default to a size adequate for the figure up to the maximum allowed by the display. The window could also be made larger or smaller via user action on its edges or corners. (Minimum/maximum limits for this are open for discussion.) For any window that is smaller than needed for the figure, scrollbars would allow the user to view any part of the figure. In other words, even if the figure might fit on the screen, the user would be free to resize and position a smaller window, and then select the region of the figure to be shown in it.

    The big change in 2 is that the window size becomes uncoupled from the figure size, apart from the initial default that matches them when possible.

  22. anntzer commented on Jun 6, 2023

    @anntzer
    Contributor

    I think this is essentially what I describe at

    Note that effectively there's another canvas size that needs to be defined (with all the bookkeeping associated -- here I just hardcoded the minimumSizeHint), which is the size below which scrollbars appear

    ?

  23. efiring commented on Jun 6, 2023

    @efiring
    Member

    Agreed. Does it make anything substantially simpler if the scrollbars are always provided, needed or not? Having them appear only when needed is a minor refinement not a requirement.

  24. timhoffm commented on Jun 7, 2023

    @timhoffm
    Member

    Automatic hiding of unneeded scrollbars is typically a GUI framework feature that we should get for free (I know that for sure for Qt, but suppose it's similar for the others as well).


    Note that effectively there's another canvas size that needs to be defined (with all the bookkeeping associated -- here I just hardcoded the minimumSizeHint), which is the size below which scrollbars appear

    Not sure whether it's the same thing, but my approach would be: The Canvas widget can have two different modes: Fixed size based on the contained figure or resizable. - At least in Qt terms, we should be able to embed that in a QScrollArea and the layout mechanism should be able to do everyting else: For the fixed sizes, the ScrollArea handles the missing/excess space. For the scaling case, I think the ScrollArea can report its viewport to the Canvas, which then can size accordingly (but t.b.c.).

  25. github-actions commented on Jun 7, 2024

    @github-actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  26. added
    keepItems to be ignored by the “Stale” Github Action
    and removed
    status: inactiveMarked by the “Stale” Github Action
    on Jun 7, 2024
  27. efiring commented on Jun 8, 2024

    @efiring
    Member

    This is still a valid feature request.

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

    API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …Difficulty: Mediumhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issuesNew featurekeepItems to be ignored by the “Stale” Github Action

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions