简体   繁体   中英

How to update plot in Matplotlib when user data is inputted (via PyQt5 button)

I've been working on this code tirelessly throughout the day, but can't seem to figure out where my logic is off. I want the user to be able to browse for a file (here they are simply pressing a button called 'Browse') that immediately updates the shown plot (here going from blue lines to red in the plot; but ultimately in my actual program going from a blank plot to a filled one, or from a filled to a different filled one).

It's inspired by an example from https://matplotlib.org/examples/user_interfaces/embedding_in_qt5.html , but I've altered it quite a bit to express my current problem in a way that is most hopefully minimal and complete. Thank you so much in advance for even taking a look.

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_())

You have 2 errors:

  • When using MyDynamicMplCanvas().update_figure() you are creating a new MyDynamicMplCanvas() object that is different from dc = MyDynamicMplCanvas(self, width = 5, height = 4, dpi = 100) , so being a local variable is eliminated a never shown.

  • The second is that the sentence P1.df is True will never be true since P1.df is False or is a list.

The solution is to make a member of the class and changes if P1.df is True: to 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()

...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM