使用 Qt Designer 表单和 PyQt5 在 QWidget 中绘制 matplotlib 图

Plotting matplotlib figure inside QWidget using Qt Designer form and PyQt5

我不明白将 matplotlib 图 link 转换为从 Qt Designer 创建的表单的最佳方法。我有一个在 QtDesigner 中创建的表单,然后通过 pyuic5 编译为 python。我的主要程序是:

import app_framework as af
import matplotlib
from PyQt5 import QtWidgets
import sys

matplotlib.use('Qt5Agg')

app = QtWidgets.QApplication(sys.argv)
form = af.MyApp()
form.show()
app.exec_()

其中 myApp 调用从 Qt Designer 创建然后由 pyuic5 (design.py) 转换的 app_framework.py 表单:

from PyQt5.QtWidgets import QApplication, QMainWindow
import design

class MyApp(QMainWindow, design.Ui_mainWindow):
   def __init(self):
       super(self.__class__, self).__init__()
       <button initializations>
       <function definitions for button callbacks>

我很困惑我可以在这个框架中的什么地方 link 一个 matplotlib 图到 QtDesigner 中预制的空小部件,或者类似的东西,这样我就可以在 GUI 中绘制新数据 window 随着事情的发生(输入文本、按下按钮等)

我在 and matplotlib's site 上找到了一些话题,但我不确定我是否理解在 Qt Designer 表单中为这个小部件创建 space 然后 [=37] 的正确过程=] 绘图,and/or 创建小部件 post hoc,然后 link 绘图和绘图。

到目前为止,我所做的是在 Qt Creator 中创建一个空的 QWidget,然后在 pyuic5 编译后,我按如下方式更改 design.py 文件:

from PyQt5 import QtCore, QtGui, QtWidgets

# **** ADDED THIS
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
# **** 

class Ui_mainWindow(object):
    def setupUi(self, mainWindow):
        <mainWindow setup stuff>
        self.centralwidget = QtWidgets.QWidget(mainWindow)

        # ****ALTERED THIS FROM self.plotWidget = QtWidgets.QWidget(self.centralWidget)
        self.plotWidget = MplWidget(self.centralWidget)
        # ***** 

        self.plotWidget.setGeometry(QtCore.QRect(20, 250, 821, 591))
        self.plotWidget.setObjectName("plotWidget")

  # **** ADDED THIS 
class MplCanvas(Canvas):
    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        Canvas.__init__(self, self.fig)
        Canvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        Canvas.updateGeometry(self)


class MplWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.canvas = MplCanvas()
 # ***********

然后 app_framework.py 为:

from PyQt5.QtWidgets import QApplication, QMainWindow
import design

class MyApp(QMainWindow, design.Ui_mainWindow):
   def __init(self):
       super(self.__class__, self).__init__()
       self.setupUi(self)
       self.pushButton_plotData.clicked.connect(self.plot_data)

    def plot_data(self):
        x=range(0, 10)
        y=range(0, 20, 2)
        self.plotWidget.canvas.ax.plot(x, y)
        self.plotWidget.canvas.draw()

我认为这会起作用,但是当我单击绘图按钮时,没有任何反应。它没有锁定,它只是不绘制任何东西。我猜我错过了在这个空的小部件中绘制 matplotlib 图 and/or canvas 的一些基本知识。

借助这本 SO post, which links to this free sample chapter 我可能需要购买的书,我找到了解决方案。正如许多其他帖子中提到的那样,需要使用 header mplwidget "Promote" 将空白 QtWidget 转换为 MplWidget。这样做之后,然后 运行ning pyuic5 命令,"from mplwidget import MplWidget" 将出现在 design.py 文件中,可以随时更新而不用担心覆盖。然后,创建一个 mplwidget.py 文件:

# Imports
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
import matplotlib

# Ensure using PyQt5 backend
matplotlib.use('QT5Agg')

# Matplotlib canvas class to create figure
class MplCanvas(Canvas):
    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        Canvas.__init__(self, self.fig)
        Canvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        Canvas.updateGeometry(self)

# Matplotlib widget
class MplWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)   # Inherit from QWidget
        self.canvas = MplCanvas()                  # Create canvas object
        self.vbl = QtWidgets.QVBoxLayout()         # Set box for plotting
        self.vbl.addWidget(self.canvas)
        self.setLayout(self.vbl)

然后,应用程序框架可以像我之前那样。当 运行 并按下绘图按钮时,图形会按预期出现。我想我基本上已经手动完成了 "Promote" QWidget 的所有工作,只是缺少 vbl 的东西,但是每次编辑 Qt Designer 表单时,所有这些都会被覆盖。

app_framework.py:

from PyQt5.QtWidgets import QApplication, QMainWindow
import design

class MyApp(QMainWindow, design.Ui_mainWindow):
   def __init(self):
       super(self.__class__, self).__init__()
       self.setupUi(self)
       self.pushButton_plotData.clicked.connect(self.plot_data)

    def plot_data(self):
        x=range(0, 10)
        y=range(0, 20, 2)
        self.plotWidget.canvas.ax.plot(x, y)
        self.plotWidget.canvas.draw()

main.py:

from PyQt5 import QtWidgets
import sys

# Local Module Imports
import app_framework as af

# Create GUI application
app = QtWidgets.QApplication(sys.argv)
form = af.MyApp()
form.show()
app.exec_()