使用pyqtgraph时出现性能问题

3

我正在尝试将一些代码从使用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变得越来越慢,那么这就不够快了。
与PlotDataItem不同,Down-sampling似乎不适用于PlotCurveItem,但我发现进行以下操作:
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,它仍然非常缓慢(每个图形需要几秒钟时间)。我真的希望能够尽可能快地绘制图像。我的目标是每个图形少于100毫秒。

然而很难确定哪里会出现延迟,对这段代码进行分析似乎很困难,因为其中一些发生在OpenGL级别,一些深入Qt代码,还有一些在pyqtgraph本身内部。

还有其他方法可以进一步加快此代码吗?

注意:我目前正在使用Python 2.7 64位、PyQt 4.10.4和通过conda安装的pyqtgraph 0.10.0,但此代码需要在Python 3.5+上同样良好地工作。


3
有趣的是,将笔宽设置为1会使绘图速度快得多。最近似乎已经有人报告了这个问题 - user3419537
@user3419537 哇,这真是有趣。本地图形系统现在比使用OpenGL更快,并且速度已经达到了一个非常可接受的水平。我能够绘制大约五十万个点,而不需要超过150毫秒来添加新的绘图集合。这是一个奇怪的问题,但我会在以后的更新中跟进它。谢谢! - bheklilr
将行宽备注加1。我想指出,您的用户界面变得越来越慢,因为您不断添加曲线到图形中,但从未删除它们。这是您真正想要的吗?如果在添加曲线项之前清除图形(使用self.axes[axis].clear()),则绘图时间保持不变。 - titusjan
1个回答

0

嗨,@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 上运行你的示例。

我们正在尝试让更多的用户尝试这个示例,并提出一些边缘情况,看看是否会出现问题;当我们获得一定的信心后,我们将合并此功能,并将其作为下一个版本的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接