Multiple live plot GUI using pyqtgraph and PyQt5

Viewed 862

I am trying to create a gui with several live plot EEG/ECG graphs (each plot on different axes). From what I understand, I need to create multiple PlotWidgets inside a grid layout.

I have created a live plot using pyqtgraph, below however I am unsure how to merge this into the GUI:

from pyqtgraph.Qt import QtGui, QtCore import pyqtgraph as pg import collections import random import time import math import numpy as np

class DynamicPlotter:

    def __init__(self, sampleinterval=0.1, timewindow=10., size=(600, 350)):
        # Data stuff
        self.interval = int(sampleinterval * 1000)
        self.bufsize = int(timewindow / sampleinterval)
        self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
        self.x = np.linspace(-timewindow, 0.0, self.bufsize)
        self.y = np.zeros(self.bufsize, dtype=float)
        # PyQtGraph stuff
        self.app = QtGui.QApplication([])
        self.plt = pg.plot(title='EEG/ECG Live Plot')
        self.plt.resize(*size)
        self.plt.showGrid(x=True, y=True)
        #self.plt.setXRange(5,20, padding=0)
        self.plt.setLabel('left', 'Amplitude', 'uVrms')
        self.plt.setLabel('bottom', 'Time', 's')
        self.curve = self.plt.plot(self.x, self.y, pen=(255, 0, 0))

        # QTimer
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updateplot)
        self.timer.start(self.interval)

    def getdata(self):
        frequency = 0.5
        noise = random.normalvariate(0., 1.)
        new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
        return new

    def updateplot(self):
        self.databuffer.append(self.getdata())
        self.y[:] = self.databuffer
        self.curve.setData(self.x, self.y)
        self.app.processEvents()

    def run(self):
        self.app.exec_()

if __name__ == '__main__':
    livePlot = DynamicPlotter(sampleinterval=0.05, timewindow=5.)
    livePlot.run()

Here is the basic GUI (3 plot widgets inside grid, and a few labels in mainWindow):

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(845, 727)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.labelTitle = QtWidgets.QLabel(self.centralwidget)
        self.labelTitle.setGeometry(QtCore.QRect(280, 0, 291, 51))
        font = QtGui.QFont()
        font.setPointSize(12)
        font.setBold(True)
        font.setWeight(75)
        self.labelTitle.setFont(font)
        self.labelTitle.setObjectName("labelTitle")
        self.labelCh1 = QtWidgets.QLabel(self.centralwidget)
        self.labelCh1.setGeometry(QtCore.QRect(20, 90, 31, 51))
        self.labelCh1.setObjectName("labelCh1")
        self.labelCh2 = QtWidgets.QLabel(self.centralwidget)
        self.labelCh2.setGeometry(QtCore.QRect(20, 180, 31, 51))
        self.labelCh2.setObjectName("labelCh2")
        self.labelCh3 = QtWidgets.QLabel(self.centralwidget)
        self.labelCh3.setGeometry(QtCore.QRect(20, 260, 31, 51))
        self.labelCh3.setObjectName("labelCh3")
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(70, 70, 741, 261))
        self.widget.setObjectName("widget")
        self.gridLayout = QtWidgets.QGridLayout(self.widget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        self.ch1PlotWidget = PlotWidget(self.widget)
        self.ch1PlotWidget.setObjectName("ch1PlotWidget")
        self.gridLayout.addWidget(self.ch1PlotWidget, 0, 0, 1, 1)
        self.ch2PlotWidget = PlotWidget(self.widget)
        self.ch2PlotWidget.setObjectName("ch2PlotWidget")
        self.gridLayout.addWidget(self.ch2PlotWidget, 1, 0, 1, 1)
        self.ch3PlotWidget = PlotWidget(self.widget)
        self.ch3PlotWidget.setObjectName("ch3PlotWidget")
        self.gridLayout.addWidget(self.ch3PlotWidget, 2, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 845, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.labelTitle.setText(_translate("MainWindow", "EEG/ECG Recording GUI"))
        self.labelCh1.setText(_translate("MainWindow", "Ch 1"))
        self.labelCh2.setText(_translate("MainWindow", "Ch 2"))
        self.labelCh3.setText(_translate("MainWindow", "Ch 3"))
from pyqtgraph import PlotWidget


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

My question is how do I integrate these two so I can plot the live graphs in each widget? Ideally I want to use a super class so I can simply import the unedited gui.

I have tried importing the gui.Ui_MainWindow into the class and then overwriting the self.plt to self.Ch1PlotWidget

from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
import gui as gui
import sys
import pyqtgraph as pg
import collections
import random
import time
import math
import numpy as np


class MainWindow(QtWidgets.QMainWindow, gui.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.setupUi(self)

        # Data stuff
        self.interval = 100
        self.bufsize = int(10 / self.interval)
        self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
        self.x = np.linspace(-10, 0.0, self.bufsize)
        self.y = np.zeros(self.bufsize, dtype=float)
        # PyQtGraph stuff
        self.app = QtGui.QApplication([])
        self.ch1PlotWidget = pg.plot(title='Live Plot')
        self.ch1PlotWidget.resize(600, 350)
        self.ch1PlotWidget.showGrid(x=True, y=True)
        # self.plt.setXRange(5,20, padding=0)
        self.ch1PlotWidget.setLabel('left', 'Amplitude', 'uVrms')
        self.ch1PlotWidget.setLabel('bottom', 'Time', 's')
        self.curve = self.ch1PlotWidget.plot(self.x, self.y, pen=(255, 0, 0))

        # QTimer
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updateplot)
        self.timer.start(self.interval)

    def getdata(self):
        frequency = 0.5
        noise = random.normalvariate(0., 1.)
        new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
        return new

    def updateplot(self):
        self.databuffer.append(self.getdata())
        self.y[:] = self.databuffer
        self.curve.setData(self.x, self.y)
        self.app.processEvents()


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

Sorry for all the code, I am just very confused on how to implement logic to the gui.

1 Answers

Here is an option where you can use both classes with minimal changes.

Change the DynamicPlotter constructor to accept a PlotWidget as a argument instead of creating a new one, since they are created and added to a layout in Ui_Mainwindow. In the MainWindow class, create a DynamicPlotter object for each plot (and keep a persistent reference, in this case I added them to a list self.plots).

class DynamicPlotter:

    def __init__(self, plot, sampleinterval=0.1, timewindow=10., size=(600, 350)):
        # Data stuff
        self.interval = int(sampleinterval * 1000)
        self.bufsize = int(timewindow / sampleinterval)
        self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
        self.x = np.linspace(-timewindow, 0.0, self.bufsize)
        self.y = np.zeros(self.bufsize, dtype=float)
        
        # PyQtGraph stuff
        self.plt = plot
        self.plt.setTitle('EEG/ECG Live Plot')
        self.plt.resize(*size)
        self.plt.showGrid(x=True, y=True)
        #self.plt.setXRange(5,20, padding=0)
        self.plt.setLabel('left', 'Amplitude', 'uVrms')
        self.plt.setLabel('bottom', 'Time', 's')
        self.curve = self.plt.plot(self.x, self.y, pen=(255, 0, 0))

        # QTimer
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updateplot)
        self.timer.start(self.interval)

    def getdata(self):
        frequency = 0.5
        noise = random.normalvariate(0., 1.)
        new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
        return new

    def updateplot(self):
        self.databuffer.append(self.getdata())
        self.y[:] = self.databuffer
        self.curve.setData(self.x, self.y)


class MainWindow(QtWidgets.QMainWindow, gui.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.setupUi(self)
        self.plots = []
        for plot in (self.ch1PlotWidget, self.ch2PlotWidget, self.ch3PlotWidget):
            self.plots.append(
                DynamicPlotter(plot, sampleinterval=0.05, timewindow=5.)
                )


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

Related