使用Qt Designer(PySide)中的Matplotlib

9
我一直在寻找一个工作示例,说明如何在保持逻辑在一个单独的文件中的情况下,在使用QT Designer创建的Pyside中嵌入matplotlib绘图。我知道网上有很多例子,但是它们实际上都没有使用QT Designer,然后创建一个单独的文件来添加逻辑,其中matplitlib绘图被添加到小部件中。我找到了一个“几乎”可用的示例http://blog.rcnelson.com/building-a-matplotlib-gui-with-qt-designer-part-1/但在我的版本中不可能“将layoutName属性从“verticalLayout”更改为“mplvl”。”
所以我有以下具体问题: 我不清楚绘图可以嵌入到Pyside Qt Designer中的哪个项目中。它是一个简单的“widget”(因为在pyside中没有可用的matplotlib widget),如果是这样,我怎样才能将绘图添加到该widget中?或者我必须在Qt Designer中创建一个'FigureCanvas'吗?这真的可能吗?如果可以,那应该怎么做?
这是我可以使用Pyside Qt Designer创建的最简单的设计,用于嵌入widget(这正确吗?)。我现在怎样在其上方添加一个matplotlib绘图?
正如在其中一个答案中建议的那样,我现在已经将QWidget提升为MyStaticMplCanvas并将QWidget的名称更改为mplvl。
使用Pyside Qt Designer自动生成的文件,并使用pyside-uic ui.ui -o ui.py -x进行编译。 ui.py长这样:
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'gui.ui'
#
# Created: Wed Apr 20 14:00:02 2016
#      by: pyside-uic 0.2.15 running on PySide 1.2.2
#
# WARNING! All changes made in this file will be lost!

from PySide import QtCore, QtGui

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(444, 530)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.mplvl = MyStaticMplCanvas(self.centralwidget)
        self.mplvl.setGeometry(QtCore.QRect(120, 190, 221, 161))
        self.mplvl.setObjectName("mplvl")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 444, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))

from mystaticmplcanvas import MyStaticMplCanvas

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

如何从单独的 .py 文件中将绘图添加到 mplvl 对象中?

2
在同一进程中不能混合使用PyQtPySide导入。我建议您从matplotlib.backends.qt_compat模块中导入所有内容,我们在其中模拟了差异。 - tacaswell
1
pyside的代码是由pyside-uic自动生成的,所以我需要保留它。除非你是在说这不可能? - Nickpick
1
我曾经遇到过类似的问题 - 尽管有一些不同之处。请查看stackoverflow上的这篇文章: https://dev59.com/6Zbfa4cB1Zd3GeqPpSM-#36669876 希望能对你有所帮助。 - K.Mulier
2个回答

8

我并不是这方面的专家,所以我的做法可能不太规范,但是以下代码可以帮助你入门:

  • 我认为最简单的添加小部件的方式是通过 QxxxxLayout
  • 然后我让你的 Plotter 继承自 FigureCanvas
  • 并告诉 matplotlib 使用 PySide

ui.py:

from PySide import QtCore, QtGui

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(533, 497)
        self.mplvl = QtGui.QWidget(Form)
        self.mplvl.setGeometry(QtCore.QRect(150, 150, 251, 231))
        self.mplvl.setObjectName("mplvl")
        self.vLayout = QtGui.QVBoxLayout()
        self.mplvl.setLayout(self.vLayout)
        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))

您只需将画布添加到QtDesigner中的mplvl即可实现此功能。

main.py:

import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4'] = 'PySide'
from matplotlib.backends.backend_qt4agg import (
    FigureCanvasQTAgg as FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
from PySide import QtGui, QtCore
import random

from weakref import proxy
from ui import Ui_Form


class Plotter(FigureCanvas):
    def __init__(self, parent):
        ''' plot some random stuff '''
        self.parent = proxy(parent)
        # random data
        data = [random.random() for i in range(10)]
        fig = Figure()
        super(Plotter,self).__init__(fig)
        # create an axis
        self.axes = fig.add_subplot(111)
        # discards the old graph
        self.axes.hold(False)
        # plot data
        self.axes.plot(data, '*-')

    def binding_plotter_with_ui(self):
        self.parent.vLayout.insertWidget(1, self)

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    Form = QtGui.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    # plotter logic and binding needs to be added here
    plotter = Plotter(ui)
    plotter.binding_plotter_with_ui()
    plotter2 = Plotter(ui)
    plotter2.binding_plotter_with_ui()
    Form.show()
    sys.exit(app.exec_())

现在,您可能需要调整FigureCanvas的大小和比例,以使其看起来符合您的要求。 您可以参考此示例其他示例来获取所需效果。

祝你好运!


澄清一下:小部件需要放置在布局中。 在这种情况下,小部件命名为mplvl,垂直布局命名为vLayout。然后上述代码将起作用。 - Nickpick
唯一的小问题是图表底部被切断了一点。有什么办法可以解决吗? - Nickpick
我建议您执行以下操作:在QtDesigner中添加第一个滚动区域,其中放置垂直布局(替换“mplvl”)。然后在此布局中添加绘图。垂直轴上的大小问题仅来自于您的“self.mplvl.setGeometry(QtCore.QRect(150, 150, 251, 231))”,这太小了。 - Silmathoron

3
请查看 matplotlib 在 QT4 中嵌入的示例:http://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html。该示例定义了几个类,实现了在 QT4 应用程序中使用 matplotlib 小部件,例如类 MyStaticMplCanvas。要在 QT Designer 中使用这个类的技巧是使用标准 QWidget,右键单击它并选择 Promote to ...,在 Promoted class name 下填写类名 MyStaticMplCanvas,在 header file 下填写此类所在的文件名(.h 扩展名已添加,但在 Python 代码中被忽略)。单击 AddPromote。现在,在 uic 编译后,Python 代码应该类似于:
from PySide import QtCore, QtGui
class Ui_Form(object):
    def setupUi(self, Form):
        ...
        self.mplvl = MyStaticMplCanvas(Form)
        ...

from mystaticmplcanvas import MyStaticMplCanvas

1
这绝对是朝着正确方向迈出的一步。我现在遇到的唯一问题是self.setParent(parent)导致了一个错误: TypeError: arguments did not match any overloaded call: QWidget.setParent(QWidget): argument 1 has unexpected type 'PySide.QtGui.QWidget' QWidget.setParent(QWidget, Qt.WindowFlags): argument 1 has unexpected type 'PySide.QtGui.QWidget' - Nickpick
1
请确保您只从PySide导入,而不是从PyQt4导入。可能您的matplotlib默认使用PyQt4。您脚本的第一行应该设置正确的后端,就像Silmathoron的答案中所示。 - Rob
我现在已经编辑了原始问题,以更好地展示事物的当前状态。 - Nickpick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接