Matplotlib figure/plot/canvas/layout hides buttons

2020-05-01 04:45发布

问题:

Matplotlib figure/plot/canvas/layout hides buttons. The picture should explain the problem: I would like the container of the plot to leave space for the buttons on the right.

Here is a minimal working code example with some commented things that I tried:

import sys
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QApplication, QDialog
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog(QDialog):
    def __init__(self, parent=None):
        super(Ui_Dialog, self).__init__(parent)
        self.setupUi(self)
        self.setupPlot()

    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(400, 300)
        Dialog.setToolTip("")
        Dialog.setStatusTip("")
        Dialog.setWhatsThis("")
        Dialog.setAccessibleName("")
        Dialog.setAccessibleDescription("")
        self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
        self.buttonBox.setGeometry(QtCore.QRect(320, 50, 81, 241))
        self.buttonBox.setOrientation(QtCore.Qt.Vertical)
        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.buttonBox.setObjectName("buttonBox")
        self.retranslateUi(Dialog)
        self.buttonBox.accepted.connect(Dialog.accept)
        self.buttonBox.rejected.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        pass

    def setupPlot(self):
        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        #self.canvas.resize(self, 5, 5)
        #self.resize(5,5)

        self.toolbar = NavigationToolbar(self.canvas, self)
        layout = QVBoxLayout()
        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)
        self.setLayout(layout)
        self.plot()

    def plot(self):
        data = [random.random() for i in range(10)]
        self.figure.clear()

        #plt.figure(num=2, figsize=(3, 3), dpi=80, facecolor='w', edgecolor='k')
        #self.figure.set_size_inches(5, 5, forward=True)

        ax = self.figure.add_subplot(111)

        #self.figure.subplots_adjust(left=1, bottom=1, right=1, top=1, wspace=1, hspace=1)

        ax.plot(data, '*-')
        self.canvas.draw()


if __name__ == '__main__':
    app = QApplication(sys.argv)

    main = Ui_Dialog()
    main.show()

    sys.exit(app.exec_())

回答1:

Instead of placing the buttonbox in absolute coordinates, you may add it to a layout, just like you did with the canvas and the toolbar. To this end, you may use another widget, which contains the canvas and toolbar. This widget can be placed in a QHBoxLayout(); the QDialogButtonBox would be added second in the layout.

import sys
#from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QApplication, QDialog
from PyQt4.QtGui import QPushButton, QVBoxLayout, QApplication, QDialog
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random

#from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt4 import QtCore, QtGui #, QtWidgets

class Ui_Dialog(QDialog):
def __init__(self, parent=None):
    super(Ui_Dialog, self).__init__(parent)
    self.setupUi(self)
    self.setupPlot()

def setupUi(self, Dialog):
    
    Dialog.setObjectName("Dialog")
    Dialog.resize(400, 300)
    self.buttonBox = QtGui.QDialogButtonBox(Dialog)
    self.buttonBox.setOrientation(QtCore.Qt.Vertical)
    self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
    self.buttonBox.setObjectName("buttonBox")

    self.buttonBox.accepted.connect(Dialog.accept)
    self.buttonBox.rejected.connect(Dialog.reject)
    QtCore.QMetaObject.connectSlotsByName(Dialog)
    
    self.setLayout(QtGui.QHBoxLayout())
    self.layout().setContentsMargins(0,0,0,0)
    

def setupPlot(self):
    self.figure = plt.figure()
    self.figure.set_facecolor("none")
    self.canvas = FigureCanvas(self.figure)
    self.canvas.setContentsMargins(0,0,0,0)
    self.widget = QtGui.QWidget()
    self.widget.setContentsMargins(0,0,0,0)

    self.toolbar = NavigationToolbar(self.canvas, self)
    self.toolbar.setContentsMargins(0,0,0,0)
    layout = QVBoxLayout()
    layout.setSpacing(0)
    layout.setContentsMargins(0,0,0,0)
    layout.addWidget(self.toolbar)
    layout.addWidget(self.canvas)
    self.widget.setLayout(layout)
    self.layout().addWidget(self.widget)
    self.layout().addWidget(self.buttonBox)
    self.plot()

def plot(self):
    data = [random.random() for i in range(10)]
    self.figure.clear()
    ax = self.figure.add_subplot(111)
    ax.plot(data, '*-')
    self.canvas.draw()

if __name__ == '__main__':
app = QApplication(sys.argv)
main = Ui_Dialog()
main.show()
sys.exit(app.exec_())

Alternatively, you can first place the toolbar in a QVBoxLayout and beneath place a widget containing the canvas and the Button box in a QHBoxLayout.

import sys
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random

from PyQt4 import QtCore, QtGui

class Ui_Dialog(QtGui.QDialog):
def __init__(self, parent=None):
    super(Ui_Dialog, self).__init__(parent)
    self.setupUi(self)

def setupUi(self, Dialog):
    
    Dialog.setObjectName("Dialog")
    Dialog.resize(400, 300)
    self.buttonBox = QtGui.QDialogButtonBox(Dialog)
    self.buttonBox.setOrientation(QtCore.Qt.Vertical)
    self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
    self.buttonBox.setObjectName("buttonBox")

    self.buttonBox.accepted.connect(Dialog.accept)
    self.buttonBox.rejected.connect(Dialog.reject)
    QtCore.QMetaObject.connectSlotsByName(Dialog)
    
    self.setLayout(QtGui.QVBoxLayout())
    self.layout().setContentsMargins(0,0,0,0)
    
    self.figure = plt.figure()
    self.figure.set_facecolor("none")
    self.canvas = FigureCanvas(self.figure)
    self.widget = QtGui.QWidget()

    self.toolbar = NavigationToolbar(self.canvas, self)
    self.layout().addWidget(self.toolbar)
    layout = QtGui.QHBoxLayout()
    layout.addWidget(self.canvas)
    layout.addWidget(self.buttonBox)
    self.widget.setLayout(layout)
    self.layout().addWidget(self.widget)
    
    self.plot()

def plot(self):
    data = [random.random() for i in range(10)]
    self.figure.clear()
    ax = self.figure.add_subplot(111)
    ax.plot(data, '*-')
    self.canvas.draw()

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Ui_Dialog()
main.show()
sys.exit(app.exec_())