Skip to content

FigureCanvas.draw() with tight_layout () needs to be called twice with Matplotlib 2.1.0 #10361

Closed

Description

@chipmuenk

Bug report

Bug summary
PyQt widgets with a Matplotlib (versions 2.1.0 and 2.1.2) canvas don't respect tight_layout() unless a redraw is triggered for the second time. The class shown below (excerpt) is instantiated in all plotting widgets in the app under https://github.com/chipmuenk/pyfda .

The full code can be found under
https://github.com/chipmuenk/pyFDA/blob/develop/pyfda/plot_widgets/mpl_widget.py

In the pyfda project, the bug is referenced as chipmuenk/pyfda#113 .

Code for reproduction

from matplotlib.figure import Figure
# the other imports are from PyQt4 / PyQt5 via a compatibility wrapper
class MplWidget(QWidget):
    """
    Construct a subwidget from pyQt Matplotlib canvas and a modified NavigationToolbar.
    """
    def __init__(self, parent):
        super(MplWidget, self).__init__(parent)
        self.fig = Figure()

        self.pltCanv = FigureCanvas(self.fig)
        self.pltCanv.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.pltCanv.setFocusPolicy(QtCore.Qt.ClickFocus)
        self.pltCanv.setFocus()
        self.pltCanv.updateGeometry()
        self.mplToolbar = MplToolbar(self.pltCanv, self) # customized toolbar

        #=============================================
        # Main plot widget layout
        #=============================================
        self.layVMainMpl = QVBoxLayout()
        self.layVMainMpl.addWidget(self.mplToolbar)
        self.layVMainMpl.addWidget(self.pltCanv)
        self.setLayout(self.layVMainMpl)
        
    def redraw(self):
        """
        Redraw the figure with new properties (grid, linewidth etc.)
        """
        # only execute when at least one axis exists -> tight_layout crashes otherwise
        if self.fig.axes:
            for ax in self.fig.axes:
                if self.mplToolbar.lock_zoom:
                    ax.axis(self.limits) # restore old limits
                else:
                    self.limits = ax.axis() # save limits
            try:
                # tight_layout() crashes with small figure sizes e.g. when upper
                # and lower limits are swapped
               self.fig.tight_layout(pad = 0.1)
            except(ValueError, np.linalg.linalg.LinAlgError):
                pass

            self.pltCanv.draw() # now (re-)draw the figure

Actual outcome
The plot is surrounded by a lot of whitespace

Expected outcome
The plot should be formatted with a tight_layout() layout. This was the case with matplotlib 1.4.2, 1.5.2, 1.5.4 and 2.0

Matplotlib version

  • Operating system: Win 7, Linux 32 and 64 bit
  • Matplotlib version: 2.1.0, 2.1.2
  • Matplotlib backend: Qt4Agg and Qt5Agg
  • Python version: 2.7, 3.5, 3.6
  • Jupyter version (if applicable): ---
  • Other libraries:

Installation via conda (default channel)

Activity

  1. added this to the milestone on Feb 1, 2018
  2. jklymak commented on Feb 1, 2018

    @jklymak
    Member

    I have some guesses as to whats wrong, but a link to a self contained example (if its too long to post here) would help.

    Have you tried self.fig = Figure(tight_layout=True) That should call tight layout whenever draw is called.

    If you don't call tight_layout does the white space dramatically decrease in size? Possibly when tight_layout is called the first time its working with a layout that has been made on a virtual canvas that is much smaller than the canvas ends up being.

  3. chipmuenk commented on Feb 2, 2018

    @chipmuenk
    Author

    Sorry for not providing a self-contained example, I haven't found the time yet to carve out a MWE from my code containing a matplotlib-widget and some dummy code. In the meantime - if you don't mind the trouble (don't hit me!) - pyfda can be installed/deinstalled easily via pip install pyfda or conda install -c Chipmuenk pyfda ...

    I've tried your suggestions:

    • Most interestingly: matplotlib 2.0 shows the same (correct) behaviour as matplotlib 1.5, the bug seems to have (re-)entered with matplotlib 2.1.0
    • Commenting out the call to tight_layout() leaves the same (to my eye, not counting pixels) amount of white space with matplotlib 1.5 and 2.1
    • I didn't know the option Figure(tight_layout=True) , but it shows exactly the same behaviour as calling tight_layout() manually for both mpl 1.5 and 2.1 in my case. However, now I can't catch the exceptions occurring when a user tries to shrink the window to a very small size, e.g.
      raise LinAlgError("Singular matrix")
      py.linalg.linalg.LinAlgError: Singular matrix

    The problem seems to be around for a while though: https://stackoverflow.com/questions/18678306/matplotlib-qt-tight-layout-doesnt-work-on-the-first-draw#18702991

    The proposed measure didn't help in my case though.

  4. chipmuenk commented on Apr 19, 2018

    @chipmuenk
    Author

    Dynamically updating the plots only when the tab has been made visible fixes this bug as well.

  5. modified the milestones: v3.0, v3.1 on Jul 9, 2018
  6. jklymak commented on Feb 9, 2019

    @jklymak
    Member

    I'm closing this, unless there is a MWE for us to see what the problem is. Thanks!

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions