python - 如何在 Qt 环境中嵌入的 matplotlib 中更快地绘制大量信号?

标签 python matplotlib draw

我正在尝试在嵌入在 Qt 环境中的 matplotlib 图中绘制大量信号。
这些图是根据 QScrollBar 更新的,它修改了我需要显示的信号部分。
我的问题是图形的更新需要很长时间,特别是因为我有 250 个信号要更新。因此,我正在寻找一种优化 EEG_plot.update 函数以减少其绘制时间的方法。
我不知道如何使用动画功能来加快流程或其他方式。
我担心的是我需要更新时间轴刻度,可能还有 y 轴标签位置。
另一件事是,如果我需要绘制的最后一段与选择的窗口大小不完全对应,我只需要绘制窗口的一部分(例如,最后一段将是 5 秒,但窗口大小是 10 秒)

我在下面给出整个脚本

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np


class Viewer(QMainWindow):
    def __init__(self, parent=None):
        super(Viewer, self).__init__()
        self.parent = parent
        #######################################
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.mainVBOX_param_scene = QVBoxLayout()
        self.mascene = plot(self)


        self.paramPlotV = QVBoxLayout()
        self.horizontalSliders  = QScrollBar(Qt.Horizontal)
        self.horizontalSliders.setFocusPolicy(Qt.StrongFocus)
        self.horizontalSliders.valueChanged.connect(self.update_plot)
        self.horizontalSliders.setMinimum(0)
        self.horizontalSliders.setMaximum(1)


        self.paramPlot = QHBoxLayout()
        l_gain = QLabel('Gain')
        self.e_gain = QLineEdit('5')
        l_win = QLabel('Window')
        self.e_win = QLineEdit('10')
        l_spacing = QLabel('vertical spacing')
        self.e_spacing = QLineEdit('10')
        l_linewidth = QLabel('linewidth')
        self.e_linewidth = QLineEdit('1')

        self.e_gain.returnPressed.connect(self.update_plot)
        self.e_win.returnPressed.connect(self.udpate_plot_plus_slider)
        self.e_spacing.returnPressed.connect(self.update_plot)
        self.e_linewidth.returnPressed.connect(self.update_plot)

        self.paramPlot.addWidget(l_gain)
        self.paramPlot.addWidget(self.e_gain)
        self.paramPlot.addWidget(l_win)
        self.paramPlot.addWidget(self.e_win)
        self.paramPlot.addWidget(l_spacing)
        self.paramPlot.addWidget(self.e_spacing)
        self.paramPlot.addWidget(l_linewidth)
        self.paramPlot.addWidget(self.e_linewidth)

        self.paramPlotV.addWidget(self.horizontalSliders)
        self.paramPlotV.addLayout(self.paramPlot)

        self.mainVBOX_param_scene.addWidget(self.mascene)
        self.mainVBOX_param_scene.addLayout(self.paramPlotV)

        self.centralWidget.setLayout(self.mainVBOX_param_scene)

        self.Fs = 1024
        self.Sigs_dict = np.random.rand(250,105*self.Fs)
        self.t = np.arange(self.Sigs_dict.shape[1])/self.Fs
        self.parent.processEvents()
        self.update()

    def updateslider(self):
        self.horizontalSliders.setMinimum(0)
        self.horizontalSliders.setMaximum(np.ceil(self.t[-1]/int(self.e_win.text()))-1)
        self.horizontalSliders.setPageStep(1)
        self.horizontalSliders.update()

    def udpate_plot_plus_slider(self):
        self.updateslider()
        self.mascene.update()

    def update_plot(self):
        self.mascene.update()

    def update(self):
        self.updateslider()
        self.mascene.modify_sigs()
        self.mascene.update()

class plot(QGraphicsView):
    def __init__(self, parent=None):
        super(plot, self).__init__(parent)
        self.parent = parent
        self.scene = QGraphicsScene(self)
        self.setScene(self.scene)
        self.figure = plt.figure(facecolor='white')#Figure()
        self.canvas = FigureCanvas(self.figure)

        self.widget = QWidget()
        self.widget.setLayout(QVBoxLayout())
        self.widget.layout().setContentsMargins(0, 0, 0, 0)
        self.widget.layout().setSpacing(0)
        self.scroll = QScrollArea(self.widget)
        self.scroll.setWidget(self.canvas)

        layout = QVBoxLayout()
        layout.addWidget(self.scroll)
        self.setLayout(layout)

    def modify_sigs(self):
        self.Sigs_dict = self.parent.Sigs_dict
        self.t = self.parent.t
        self.Fs= self.parent.Fs


    def update(self):
        win_num = self.parent.horizontalSliders.value()
        self.figure.clear()
        plt.figure(self.figure.number)
        plt.subplots_adjust(left=0.1, bottom=0.01, right=1, top=1, wspace=0.0 , hspace=0.0 )
        self.axes = plt.subplot(1, 1, 1)
        gain = float(self.parent.e_gain.text())
        win= float(self.parent.e_win.text())
        self.spacing = float(self.parent.e_spacing.text())
        linewidth = float(self.parent.e_linewidth.text())
        ts = int(win*(win_num) * self.Fs)
        te = ts + int(win * self.Fs)
        if te > len(self.t):
            te=len(self.t)
        for i in range(self.Sigs_dict.shape[0]):
            line, = plt.plot(self.t[ts:te], gain*(self.Sigs_dict[i,ts:te]-np.mean(self.Sigs_dict[i,ts:te]))+i*self.spacing, linewidth=linewidth  )


        self.axes.autoscale(enable=True, axis='both', tight=True)
        self.axes.set_ylim((-self.spacing,(self.Sigs_dict.shape[0]+1)*self.spacing))
        self.axes.set_xlim((ts/ self.Fs, ts / self.Fs + win ))

        self.axes.set_yticks(np.arange(self.Sigs_dict.shape[0]) * self.spacing)
        self.axes.set_yticklabels([str(n) for n in np.arange(self.Sigs_dict.shape[0])])



        self.canvas.setGeometry(0, 0, self.parent.width()-100, (self.parent.height()-100)*self.spacing)
        self.canvas.draw_idle()


def main():
    app = QApplication(sys.argv)
    app.setStyle('Windows')
    ex = Viewer(app)
    ex.showMaximized()
    sys.exit(app.exec())


if __name__ == '__main__':
    main()

更新

我做了一个新的实现,我尝试更新数据而不是每次都重新绘制所有图形(update_set_data 函数),我没有绘制曲线的所有点(例如,如果点数 > 10000 点,我只取其中的 50%)我使用了 decimate = len(self.t[ts:te]) // 10000 + 1计算抽取,最后当用户拖动 slider 时我不会重新绘制图形。

当我使用旧版本时,我有时间更新图形:
time old: 4.148899078369141
time old: 4.117990255355835
time old: 4.152893781661987

使用新版本,我得到:
time new: 2.0400094985961914
time new: 2.0248610973358154
time new: 2.0305933952331543

我不得不说,我预计会减少 50% 以上的时间。
有人有想法进一步优化吗?
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
import time

class Viewer(QMainWindow):
    def __init__(self, parent=None):
        super(Viewer, self).__init__()
        self.parent = parent
        #######################################
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.mainVBOX_param_scene = QVBoxLayout()
        self.mascene = plot(self)


        self.paramPlotV = QVBoxLayout()
        self.horizontalSliders  = QScrollBar(Qt.Horizontal)
        self.horizontalSliders.setFocusPolicy(Qt.StrongFocus)
        self.horizontalSliders.valueChanged.connect(self.sliderReleasedfun)
        self.horizontalSliders.sliderPressed.connect(self.sliderPressedfun)
        self.horizontalSliders.sliderMoved.connect(self.sliderMovedfun)
        self.horizontalSliders.sliderReleased.connect(self.sliderReleasedfun)
        self.horizontalSliders.setMinimum(0)
        self.horizontalSliders.setMaximum(1)


        self.paramPlot = QHBoxLayout()
        l_gain = QLabel('Gain')
        self.e_gain = QLineEdit('5')
        l_win = QLabel('Window')
        self.e_win = QLineEdit('10')
        l_spacing = QLabel('vertical spacing')
        self.e_spacing = QLineEdit('10')
        l_linewidth = QLabel('linewidth')
        self.e_linewidth = QLineEdit('1')

        self.e_gain.returnPressed.connect(self.update_plot)
        self.e_win.returnPressed.connect(self.udpate_plot_plus_slider)
        self.e_spacing.returnPressed.connect(self.update_plot)
        self.e_linewidth.returnPressed.connect(self.update_plot)

        self.paramPlot.addWidget(l_gain)
        self.paramPlot.addWidget(self.e_gain)
        self.paramPlot.addWidget(l_win)
        self.paramPlot.addWidget(self.e_win)
        self.paramPlot.addWidget(l_spacing)
        self.paramPlot.addWidget(self.e_spacing)
        self.paramPlot.addWidget(l_linewidth)
        self.paramPlot.addWidget(self.e_linewidth)

        self.paramPlotV.addWidget(self.horizontalSliders)
        self.paramPlotV.addLayout(self.paramPlot)

        self.mainVBOX_param_scene.addWidget(self.mascene)
        self.mainVBOX_param_scene.addLayout(self.paramPlotV)

        self.centralWidget.setLayout(self.mainVBOX_param_scene)

        self.Fs = 1024
        self.Sigs_dict = np.random.rand(250,105*self.Fs)
        self.t = np.arange(self.Sigs_dict.shape[1])/self.Fs
        self.parent.processEvents()
        self.update()

    def sliderPressedfun(self):
        self.horizontalSliders.valueChanged.disconnect()

    def sliderMovedfun(self,e):
        self.horizontalSliders.setValue(e)

    def sliderReleasedfun(self):
        self.horizontalSliders.valueChanged.connect(self.movesliderfun)
        self.movesliderfun()

    def movesliderfun(self):
        t0 = time.time()

        self.horizontalSliders.setEnabled(False)
        self.update_data()
        self.horizontalSliders.setEnabled(True)
        print('time new:', time.time()-t0)

    def updateslider(self):
        self.horizontalSliders.setMinimum(0)
        self.horizontalSliders.setMaximum(np.ceil(self.t[-1]/int(self.e_win.text()))-1)
        self.horizontalSliders.setPageStep(1)
        self.horizontalSliders.update()

    def udpate_plot_plus_slider(self):
        self.updateslider()
        self.mascene.update()

    def update_plot(self):
        self.mascene.update()

    def update_data(self):
        self.mascene.update_set_data()

    def update(self):
        self.updateslider()
        self.mascene.modify_sigs()
        self.mascene.update()

class plot(QGraphicsView):
    def __init__(self, parent=None):
        super(plot, self).__init__(parent)
        self.parent = parent
        self.scene = QGraphicsScene(self)
        self.setScene(self.scene)
        self.figure = plt.figure(facecolor='white')#Figure()
        self.canvas = FigureCanvas(self.figure)

        self.widget = QWidget()
        self.widget.setLayout(QVBoxLayout())
        self.widget.layout().setContentsMargins(0, 0, 0, 0)
        self.widget.layout().setSpacing(0)
        self.scroll = QScrollArea(self.widget)
        self.scroll.setWidget(self.canvas)

        layout = QVBoxLayout()
        layout.addWidget(self.scroll)
        self.setLayout(layout)
        self.win=10

    def modify_sigs(self):
        self.Sigs_dict = self.parent.Sigs_dict
        self.t = self.parent.t
        self.Fs= self.parent.Fs

    def update_set_data(self):
        win_num = self.parent.horizontalSliders.value()
        gain = float(self.parent.e_gain.text())
        win= float(self.parent.e_win.text())
        if not self.spacing == float(self.parent.e_spacing.text()):
            self.spacing = float(self.parent.e_spacing.text())
            spacing = True
        else:
            spacing = False
        self.linewidth = float(self.parent.e_linewidth.text())

        ts = int(self.win * (win_num) * self.Fs)
        te = ts + int(self.win * self.Fs)
        if te > len(self.t):
            diff = te - len(self.t)
            ts = ts - diff
            te = len(self.t)

        decimate = len(self.t[ts:te]) // 10000 + 1

        for i in range(self.Sigs_dict.shape[0]):
            self.Lines[i].set_data(self.t[ts:te:decimate], gain*(self.Sigs_dict[i,ts:te:decimate]-np.mean(self.Sigs_dict[i,ts:te:decimate]))+i*self.spacing )
            self.Lines[i].set_linewidth(self.linewidth)

        if spacing:
            self.axes.set_ylim((-self.spacing,(self.Sigs_dict.shape[0]+1)*self.spacing))
            self.axes.set_yticks(np.arange(self.Sigs_dict.shape[0]) * self.spacing)
            self.axes.set_yticklabels([str(n) for n in np.arange(self.Sigs_dict.shape[0])])



        self.axes.set_xlim((ts/ self.Fs, ts / self.Fs + win ))
        # self.canvas.draw_idle()
        self.canvas.draw()

    def update(self):
        win_num = self.parent.horizontalSliders.value()
        self.figure.clear()
        plt.figure(self.figure.number)
        plt.subplots_adjust(left=0.1, bottom=0.01, right=1, top=1, wspace=0.0 , hspace=0.0 )
        self.axes = plt.subplot(1, 1, 1)
        gain = float(self.parent.e_gain.text())
        win= float(self.parent.e_win.text())
        self.spacing = float(self.parent.e_spacing.text())
        linewidth = float(self.parent.e_linewidth.text())
        ts = int(self.win * (win_num) * self.Fs)
        te = ts + int(self.win * self.Fs)
        if te > len(self.t):
            diff = te - len(self.t)
            ts = ts - diff
            te = len(self.t)
        decimate = len(self.t[ts:te]) // 10000 + 1
        self.Lines = []
        for i in range(self.Sigs_dict.shape[0]):
            line, = plt.plot(self.t[ts:te:decimate], gain*(self.Sigs_dict[i,ts:te:decimate]-np.mean(self.Sigs_dict[i,ts:te:decimate]))+i*self.spacing, linewidth=linewidth  )
            self.Lines.append(line)

        self.axes.autoscale(enable=True, axis='both', tight=True)
        self.axes.set_ylim((-self.spacing,(self.Sigs_dict.shape[0]+1)*self.spacing))
        self.axes.set_xlim((ts/ self.Fs, ts / self.Fs + win ))

        self.axes.set_yticks(np.arange(self.Sigs_dict.shape[0]) * self.spacing)
        self.axes.set_yticklabels([str(n) for n in np.arange(self.Sigs_dict.shape[0])])



        self.canvas.setGeometry(0, 0, self.parent.width()-100, (self.parent.height()-100)*self.spacing)
        self.canvas.draw_idle()


def main():
    app = QApplication(sys.argv)
    app.setStyle('Windows')
    ex = Viewer(app)
    ex.showMaximized()
    sys.exit(app.exec())


if __name__ == '__main__':
    main()

最佳答案

您可以尝试使用多线程,
这样您就可以在许多子代码中破坏整个代码
并且您的所有代码都将使用多线程同时运行

关于python - 如何在 Qt 环境中嵌入的 matplotlib 中更快地绘制大量信号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58452078/

相关文章:

Python Altair 渲染错误 - Javascript 错误 : too much recursion

python - Pandas 数据帧行为: list(df) versus len(df)

iphone - 初学者 iphone 问题 : drawing a rectangle. 我做错了什么?

c++ - 对象列表中的变量不会改变

Android WebView 在 scrollTo() 之后损坏绘图

python - 无法像使用 virtualenv 一样使用 venv 定义 python 包(版本)

python - Google Cloud Datastore - 是否可以为单个根实体使用事务?

python - 将图表添加到 matplotlib 子网格

Python,matplotlib : how to set tick label values to their logarithmic values

matplotlib - 在 jupyter notebook 中隐藏 matplotlib 描述