Search code examples
pythonmatplotlibpyqt4toolbarfigure

Matplotlib NavigationToolbar overlapping plot (within PyQt4 embedding)


I have a PyQt window that needs to have both a plot and navigation toolbar. However, I can't seem to get the two objects to mesh together; i.e., with the toolbar on top of the plot, expanding the same horizontal length, scaling appropriately, and not overlapping. Below is my minimum, working example. thank you so much for taking a look!

import sys, os

from pyface.qt import QtGui, QtCore
os.environ['ETS_TOOLKIT'] = 'qt4'

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import matplotlib.pyplot as plt

import random

class App(QtGui.QMainWindow):

    def __init__(self):
        super(App, self).__init__()
        self.left = 10
        self.top = 10
        self.title = 'Minimum, working example'
        self.width = 640
        self.height = 400
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        m = PlotCanvas(self, width=5, height=4)
        m.move(0,0)
        self.show()


class PlotCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        FigureCanvas.__init__(self, fig)
        self.setParent(parent)
        FigureCanvas.updateGeometry(self)

        ##########################
        #trouble seems to be here:
        fig.set_tight_layout(True)
        self.canvas = FigureCanvas(fig)
        self.toolbar = NavigationToolbar(self.canvas, self)
        ##########################

        self.plot()


    def plot(self):
        data = [random.random() for i in range(25)]
        ax = self.figure.add_subplot(111)
        ax.plot(data, 'r-')
        ax.set_title("Is this getting overlapped by the toolbar?")
        self.draw()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

Solution

  • When you use self.canvas = FigureCanvas (fig) you are creating another FigureCanvas, why create another ?, it is not necessary.

    Why are they overlapping?

    The position of a child widget is relative to the parent, in this case it is self.toolbar is the children of self.canvas so the self.toolbar will be within the self.canvas.

    In order to handle the position and size of the widgets, Qt offers the layouts, and in this case it is necessary. On the other hand, there is no need for the toolbar to be inside the PlotCanvas class. And finally when using QMainWindow you should not place the widgets directly since it has a custom layout so you must create a centralwidget.

    import sys, os
    
    from pyface.qt import QtGui, QtCore
    os.environ['ETS_TOOLKIT'] = 'qt4'
    
    from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
    from matplotlib.figure import Figure
    import matplotlib.pyplot as plt
    
    import random
    
    class App(QtGui.QMainWindow):
        def __init__(self):
            super(App, self).__init__()
            self.left = 10
            self.top = 10
            self.title = 'Minimum, working example'
            self.width = 640
            self.height = 400
            self.initUI()
    
        def initUI(self):
            self.setWindowTitle(self.title)
            self.setGeometry(self.left, self.top, self.width, self.height)
            centralwidget = QtGui.QWidget()
            self.setCentralWidget(centralwidget)
            m = PlotCanvas(self, width=5, height=4)
            toolbar =  NavigationToolbar(m, self)
            vbl = QtGui.QVBoxLayout(centralwidget)
            vbl.addWidget(toolbar)
            vbl.addWidget(m)
            self.show()
    
    
    class PlotCanvas(FigureCanvas):
        def __init__(self, parent=None, width=5, height=4, dpi=100):
            fig = Figure(figsize=(width, height), dpi=dpi)
            self.axes = fig.add_subplot(111)
            FigureCanvas.__init__(self, fig)
            self.setParent(parent)
            self.updateGeometry()
            fig.set_tight_layout(True)
            self.plot()
    
        def plot(self):
            data = [random.random() for i in range(25)]
            ax = self.figure.add_subplot(111)
            ax.plot(data, 'r-')
            ax.set_title("Is this getting overlapped by the toolbar?")
            self.draw()
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        ex = App()
        sys.exit(app.exec_())
    

    enter image description here