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

lau*_*ack 13 python matplotlib pyqt5 matplotlib-widget

我不明白将matplotlib图形链接到从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_()
Run Code Online (Sandbox Code Playgroud)

其中myApp调用从Qt Designer创建的app_framework.py表单,然后由pyuic5(design.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>
Run Code Online (Sandbox Code Playgroud)

我很困惑在这个框架中我可以将matplotlib图形链接到QtDesigner中的预制空窗口小部件,或类似的东西,这样我就可以在GUI窗口中绘制新数据(事情发生,按钮按下)等)

我在SOmatplotlib的网站上找到了一些线程,但我不确定我是否理解在Qt Designer表单中为这个小部件创建空间的正确过程,然后链接一个绘图,和/或创建一个小部件post hoc和然后链接和绘图.

到目前为止我所做的是在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()
 # ***********
Run Code Online (Sandbox Code Playgroud)

然后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()
Run Code Online (Sandbox Code Playgroud)

我认为这会有效,但当我点击情节按钮时,没有任何反应.它没有锁定,它只是没有绘制任何东西.我猜我错过了在这个空小部件中绘制matplotlib图形和/或画布的基本内容.

lau*_*ack 14

我通过这篇SO帖子的帮助找到了解决方案,该帖子链接到我可能需要购买的一本书的免费样本章节.正如许多其他帖子中提到的那样,需要使用头部mplwidget将空白QtWidget"推广"到MplWidget.执行此操作,然后运行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)
Run Code Online (Sandbox Code Playgroud)

然后,应用程序框架可以像我之前一样.运行并按下绘图按钮时,图形将按预期显示.我认为我基本上已经做了所有事情来手动"推广"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()
Run Code Online (Sandbox Code Playgroud)

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_()
Run Code Online (Sandbox Code Playgroud)

  • 在我的 Mac 上,我必须将 'import matplotlib' 和 'matplotlib.use('QT5Agg')' 放在其他 matplotlib 导入之前。当代码写在答案中时,它生成了一个警告说:这个对 matplotlib.use() 的调用没有效果,因为已经选择了后端;matplotlib.use() 必须在 *before* pylab、matplotlib.pyplot 或 matplotlib.backends 首次导入之前调用。后端*最初*通过以下代码设置为“MacOSX”: ... ... from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas (2认同)
  • 在 Qt Designer 中,将 Widget 对象从容器组拖放到设计器界面上。右键单击该对象并选择升级到。将出现“Promoted Widgets”对话框,允许您选择“基类名称”(默认值: QWidget )和“Promoted 类名称”。选择 MplWidget 作为升级的类名,Qt Designer 建议头文件名 mplwidget.h。现在,当您从 .ui 文件构建 .py 文件时,您会在 .py 文件末尾看到代码行“from mplwidget import MplWidget”。然后您可以编写 mplwidget.py 文件,如本示例所示。 (2认同)