pyqtgraph 的性能问题

Posted

技术标签:

【中文标题】pyqtgraph 的性能问题【英文标题】:Performance issues with pyqtgraph 【发布时间】:2018-02-22 16:21:57 【问题描述】:

我正在尝试将一些代码从使用 matplotlib 转换为 pyqtgraph,因为它应该更高效,并且它提供了更多的交互功能。我的代码大部分都被转换了,但我遇到了它运行缓慢的问题。一些重现此的代码:

import numpy as np
import qtpy.QtWidgets as qt
import pyqtgraph as pg


class GraphWidget(qt.QWidget):
    """A widget for simplifying graphing tasks

    :param qt.QWidget parent:
    :param Dict[str, dict] layout: A mapping from title to row/col/rowspan/colspan kwargs
    """
    def __init__(self, parent, layout_spec):
        super(GraphWidget, self).__init__(parent=parent)

        self.axes = 
        glw = pg.GraphicsLayoutWidget(parent=self)
        for name, layout in layout_spec.items():
            self.axes[name] = pg.PlotItem(name=name, title=name)
            glw.addItem(self.axes[name], **layout)
        box_layout = qt.QVBoxLayout()
        box_layout.addWidget(glw, 1)
        self.setLayout(box_layout)

    @property
    def normal_pen(self):
        return pg.mkPen(color='w', width=2)

    @property
    def good_pen(self):
        return pg.mkPen(color='g', width=2)

    @property
    def bad_pen(self):
        return pg.mkPen(color='r', width=2)

    def plot(self, mode, x, y, axis):
        if mode == 'normal':
            pen = self.normal_pen
        elif mode == 'good':
            pen = self.good_pen
        elif mode == 'bad':
            pen = self.bad_pen

        plot_item = pg.PlotCurveItem(x, y, pen=pen)
        self.axes[axis].addItem(plot_item)


if __name__ == '__main__':
    import random
    import time

    # qt.QApplication.setGraphicsSystem('opengl')

    app = qt.QApplication([])
    window = qt.QWidget(parent=None)
    layout = qt.QVBoxLayout()

    gw = GraphWidget(
        window,
        
            'A': dict(row=1, col=1, rowspan=2),
            'B': dict(row=1, col=2),
            'C': dict(row=2, col=2),
            'D': dict(row=1, col=3),
            'E': dict(row=2, col=3),
            'F': dict(row=1, col=4),
            'G': dict(row=2, col=4),
        
    )
    layout.addWidget(gw, 1)

    def plot():
        start = time.time()
        for axis in 'ABCDEFG':
            gw.plot(
                random.choice(['normal', 'good', 'bad']),
                np.arange(2000),
                np.random.rand(2000),
                axis,
            )
        # necessary because without it, the "plotting" completes in ms, 
        # but the UI doesn't update for a while still
        app.processEvents()
        print('Plotting time: '.format(time.time() - start))

    button = qt.QPushButton(parent=window, text='Plot')
    button.pressed.connect(plot)
    layout.addWidget(button)

    window.setLayout(layout)
    window.showMaximized()
    app.exec_()

选择绘图的数量和布局以及点的数量以反映我的实际用例。如果我按原样运行并单击绘图按钮两次,我会看到

Plotting time: 3.61599993706
Plotting time: 7.04699993134

我停在这一点上,因为一般的应用程序突然变得非常慢,并且需要几秒钟才能关闭。如果我取消注释启用 opengl 渲染的一行,我可以轻松地运行它更多次,它看起来像

Plotting time: 0.0520000457764
Plotting time: 0.328999996185
Plotting time: 0.453000068665
Plotting time: 0.55999994278
Plotting time: 0.674000024796
Plotting time: 1.21900010109
Plotting time: 0.936000108719
Plotting time: 1.06100010872
Plotting time: 1.19899988174
Plotting time: 1.35100007057

在这一点上,我也可以看出这里报告的时间并不准确,UI 需要比这些时间更长的时间才能真正反映更新。

这是我能够处理的相当典型的图表数量,这个应用程序可以在大约 20 秒内轻松查看 16 组图表(每个轴多一条线)。如果我的 UI 越来越慢,这还不够快。

下采样似乎不适用于 PlotCurveItem(与 PlotDataItem 不同),但我确实发现这样做

plot_item = pg.PlotCurveItem(x, y, pen=pen, connect='pairs')

导致更快的时间:

Plotting time: 0.0520000457764
Plotting time: 0.0900001525879
Plotting time: 0.138000011444
Plotting time: 0.108000040054
Plotting time: 0.117000102997
Plotting time: 0.12299990654
Plotting time: 0.143000125885
Plotting time: 0.15499997139

不过,这对我来说似乎仍然很慢,我想知道是否可以采取任何措施来进一步加快速度。如果我不将它设置为 opengl,它仍然非常慢(每个情节大约几秒钟)。我真的在寻找尽可能快的绘图速度。我的目标是每幅图

虽然很难说出减速的真正原因,但分析这段代码似乎很困难,因为其中一些发生在 OpenGL 级别,一些发生在 Qt 代码内部,还有一些发生在 pyqtgraph 本身中。

还有其他方法可以进一步加快这段代码的速度吗?

注意:我目前使用的是通过 conda 安装的 Python 2.7 64 位、PyQt 4.10.4 和 pyqtgraph 0.10.0,尽管此代码需要在 Python 3.5+ 上同样有效。

【问题讨论】:

有趣的是,将笔宽设置为 1 会导致绘图速度更快。最近好像有人举报了这个issue。 @user3419537 这很有趣。本机图形系统现在比使用 OpenGL 更快,并且处于更可接受的水平。我能够绘制大约 50 万个点,而不需要超过 150 毫秒来添加一组新的绘图。这是一个奇怪的问题,但我会在以后的更新中关注它。谢谢! +1 到线宽备注。我想指出你的 UI 越来越慢,因为你不断地向图中添加曲线,但从不删除它们。那是你真正想要的吗?如果您清除绘图(在添加曲线项之前使用self.axes[axis].clear()),您的绘图时间将保持不变。 【参考方案1】:

嗨@bheklilr,这是 pyqtgraph 中的一个已知问题,并且通过扩展 Qt 框架。意外地,pyqtgraph 贡献者遇到了一种不同的绘图方法,该方法在绘制大于 1 像素的线条时不会对性能产生影响。这是实现此解决方法的 PR 草案。

https://github.com/pyqtgraph/pyqtgraph/pull/2011

如果你稍微修改你的例子,你可以恢复性能:

plot_item = pg.PlotCurveItem(x, y, pen=pen, skipFiniteCheck=True)

您需要传递 skipFiniteCheck=True 参数,而不是使用非有限值,当然,还要在 PR 上运行您的示例。

我们正在尝试让更多用户使用此示例来尝试提出出现问题的边缘情况;当我们有信心时,我们将合并此功能并将其作为下一个版本的一部分。

【讨论】:

以上是关于pyqtgraph 的性能问题的主要内容,如果未能解决你的问题,请参考以下文章

如何将 3d GLViewWidget 插入包含 2d PyQtGraph 图的窗口中

PyQtGraph:问题循环通过绘制数据切片

pyqtgraph 自定义缩放问题

pyqtgraph:在 GLSurfacePlotItem 对象上设置 smooth=False 的问题

PyQtGraph 崩溃 python

pyqtgraph - ImportError:没有名为 pyqtgraph 的模块