输入用户数据时如何更新 Matplotlib 中的绘图(通过 PyQt5 按钮)

Posted

技术标签:

【中文标题】输入用户数据时如何更新 Matplotlib 中的绘图(通过 PyQt5 按钮)【英文标题】:How to update plot in Matplotlib when user data is inputted (via PyQt5 button) 【发布时间】:2018-06-14 04:19:17 【问题描述】:

我一整天都在不知疲倦地处理这段代码,但似乎无法弄清楚我的逻辑哪里出了问题。我希望用户能够浏览一个文件(在这里他们只需按下一个名为“浏览”的按钮),它会立即更新显示的图(这里从图中的蓝线变为红色;但最终在我的实际程序中从空白地块到填充地块,或从填充地块到不同的填充地块)。

它的灵感来自https://matplotlib.org/examples/user_interfaces/embedding_in_qt5.html 中的一个示例,但我对其进行了相当多的修改,以最希望最小化和完整的方式表达我当前的问题。非常感谢您甚至看一看。

import sys
import os
import random
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QGridLayout, QFileDialog, QPushButton

from numpy import arange
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


class MyMplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)

        self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

    def compute_initial_figure(self):
        pass


class MyDynamicMplCanvas(MyMplCanvas):
    """A canvas that updates itself every second with a new plot."""

    def __init__(self, *args, **kwargs):
        MyMplCanvas.__init__(self, *args, **kwargs)
        #timer = QtCore.QTimer(self)
        #timer.timeout.connect(self.update_figure)
        #timer.start(1000)

    def compute_initial_figure(self):
        self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'b')

    def update_figure(self):
        print('update')
        # Build a list of 4 random integers between 0 and 10 (both inclusive)
        l = [random.randint(0, 10) for i in range(4)]
        self.axes.cla()
        if P1.df is True:
            self.axes.plot(P1.df, l, 'r') #should change to red, but doesn't
        else:
            self.axes.plot([0, 1, 2, 3], l, 'b')
        self.draw()

class P1(QtWidgets.QWidget):

    df = False #datafile is originally empty

    def __init__(self, parent=None):
        super(P1, self).__init__(parent)
        layout = QGridLayout(self)

        self.button_browse = QPushButton('Browse', self)
        self.button_browse.clicked.connect(self.browseFile)
        layout.addWidget(self.button_browse, 1, 1, 1, 1)
        self.button_browse.show()

        dc = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
        layout.addWidget(dc, 2, 1, 1, 1)

    def browseFile(self): #user presses browse to load new datafile
        print(P1.df)
        P1.df = [0, 1, 2, 3] 
        print(P1.df)
        #loading new datafile needs to update plot:
        MyDynamicMplCanvas().update_figure()



class MainWindow(QtWidgets.QMainWindow):    
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.stack = QtWidgets.QStackedWidget(self)
        P1f = P1(self)
        self.stack.addWidget(P1f)
        self.setCentralWidget(self.stack)

if __name__ == '__main__':
    qApp = QtWidgets.QApplication(sys.argv)
    aw = MainWindow()
    aw.show()
    sys.exit(qApp.exec_())

【问题讨论】:

【参考方案1】:

您有 2 个错误:

使用MyDynamicMplCanvas().update_figure() 时,您将创建一个不同于dc = MyDynamicMplCanvas(self, width = 5, height = 4, dpi = 100) 的新MyDynamicMplCanvas() 对象,因此消除了作为局部变量的可能性,从未显示过。

第二个是P1.df是True这句话永远不会是真的,因为P1.df是False或者是一个列表。

解决办法是做一个类的成员,把if P1.df is True:改成if P1.df:

class MyDynamicMplCanvas(MyMplCanvas):
    ...

    def update_figure(self):
        print('update')
        # Build a list of 4 random integers between 0 and 10 (both inclusive)
        l = [random.randint(0, 10) for i in range(4)]
        self.axes.cla()
        if P1.df:
            self.axes.plot(P1.df, l, 'r') #should change to red, but doesn't
        else:
            self.axes.plot([0, 1, 2, 3], l, 'b')
        self.draw()

class P1(QtWidgets.QWidget):
    df = False

    def __init__(self, parent=None):
        ...

        self.dc = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
        layout.addWidget(self.dc, 2, 1, 1, 1)

    def browseFile(self): #user presses browse to load new datafile
        P1.df = [0, 1, 2, 3]
        #loading new datafile needs to update plot:
        self.dc.update_figure()

...

【讨论】:

再次感谢@eyllanesc!完美的解释。

以上是关于输入用户数据时如何更新 Matplotlib 中的绘图(通过 PyQt5 按钮)的主要内容,如果未能解决你的问题,请参考以下文章

如何在数据文件更改时动态更新我的 matplotlib 图形?

更新 matplotlib 中的行

如何从matplotlib中的图形中检索颜色条实例

如何在 tkinter 中更新由 matplotlib 创建的图形

如何更新用户表单中的条目?

如何在更新数据库时更新数据集