使用 Qt Designer 表单和 PyQt5 在 QWidget 中绘制 matplotlib 图形
Posted
技术标签:
【中文标题】使用 Qt Designer 表单和 PyQt5 在 QWidget 中绘制 matplotlib 图形【英文标题】:Plotting matplotlib figure inside QWidget using Qt Designer form and PyQt5 【发布时间】:2021-08-27 05:19:01 【问题描述】:我不明白将 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_()
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>
我很困惑在这个框架中我可以将 matplotlib 图形链接到 QtDesigner 中的预制空小部件或类似的东西,以便我可以在发生事情时在 GUI 窗口中绘制新数据(输入的文本、按钮按下等)
我在 SO 和 matplotlib's site 上找到了一些线程,但我不确定我是否理解在 Qt Designer 表单中为此小部件创建空间然后链接绘图的正确过程,和/或事后创建一个小部件,然后链接和绘图。
到目前为止,我所做的是在 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 图形和/或画布的基本知识。
【问题讨论】:
【参考方案1】:我通过SO post 的帮助找到了解决方案,它链接到我可能需要购买的一本书的free sample chapter。正如许多其他帖子中提到的那样,需要使用标头 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)
然后,应用程序框架可以像我之前一样。运行并按下绘图按钮时,该图按预期显示。我想我基本上已经做了所有事情来手动“提升”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_()
【讨论】:
在我的 Mac 上,我必须在其他 matplotlib 导入之前放置 'import matplotlib' 和 'matplotlib.use('QT5Agg')' 行。由于代码写在答案中,它产生了一个警告说:这个对 matplotlib.use() 的调用没有效果,因为已经选择了后端; matplotlib.use() 必须在 pylab、matplotlib.pyplot 或matplotlib.backends 首次导入之前调用。后端最初通过以下代码设置为“MacOSX”: ... ... from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas 究竟如何“使用标头 mplwidget 将空白 QtWidget 提升为 MplWidget。”? 在 Qt Designer 中,将 Widget 对象从容器组拖放到您的设计器表面。右键单击对象并选择提升到。出现一个Promoted Widgets 对话框,允许您选择基类名称(默认: QWidget )和Promoted 类名称。选择 MplWidget 作为您提升的类名,Qt Designer 建议头文件名 mplwidget.h。现在,当您从 .ui 文件构建 .py 文件时,您会在 .py 文件的末尾获得代码行“from mplwidget import MplWidget”。然后你可以编写你的 mplwidget.py 文件,如本例所示。 如果你使用包并从基础模块调用 Python Qt GUI,就像我在 Eclipse 中使用 PyDev 时一样,提升的类名仍然是 MplWidget,但头文件是没有扩展名:src.MyQtGui.mplwidget(最后没有.py)以上是关于使用 Qt Designer 表单和 PyQt5 在 QWidget 中绘制 matplotlib 图形的主要内容,如果未能解决你的问题,请参考以下文章
Qt Designer 和 PyQt5 自定义插件加载 TypeError
pyqt5.8.2没有qt Designer和assistant exe
PyQt5 keyPressEvent 不适用于终止 App Qt Designer