Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python pyqtgraph/PlotCurveItem实时可视化瓶颈_Python_C++_Qt_Pyqt5_Pyqtgraph - Fatal编程技术网

Python pyqtgraph/PlotCurveItem实时可视化瓶颈

Python pyqtgraph/PlotCurveItem实时可视化瓶颈,python,c++,qt,pyqt5,pyqtgraph,Python,C++,Qt,Pyqt5,Pyqtgraph,我目前正在使用pyqtgraph可视化64个独立数据跟踪/绘图的实时数据。虽然速度非常好,但我注意到如果样本缓冲区长度超过2000点,速度会严重减慢。分析以下代码可以得出functions.py:1440(arrayToQPath)似乎具有重大影响: import numpy import cProfile import logging import pyqtgraph as pg from PyQt5 import QtCore,uic from PyQt5.QtGui import * f

我目前正在使用pyqtgraph可视化64个独立数据跟踪/绘图的实时数据。虽然速度非常好,但我注意到如果样本缓冲区长度超过2000点,速度会严重减慢。分析以下代码可以得出functions.py:1440(arrayToQPath)似乎具有重大影响:

import numpy
import cProfile
import logging

import pyqtgraph as pg
from PyQt5 import QtCore,uic
from PyQt5.QtGui import *
from PyQt5.QtCore import QRect, QTimer


def program(columns=8, samples=10000, channels=64):
    app = QApplication([])
    win = pg.GraphicsWindow()
    pg.setConfigOptions(imageAxisOrder='row-major')
    win.resize(1280,768)
    win.ci.layout.setSpacing(0)
    win.ci.layout.setContentsMargins(0,0,0,0)

    data            = numpy.zeros((samples, channels+1))
    plots           = [win.addPlot(row=i/columns+1,col=i%columns) for i in range(channels)]
    curves          = list()

    x = numpy.linspace(0, 1, samples, endpoint=True)
    f = 2 # Frequency in Hz
    A = 1 # Amplitude in Unit
    y = A * numpy.sin(2*numpy.pi*f*x).reshape((samples,1)) # Signal

    data[:,0]   = x
    data[:,1:]  = numpy.repeat(y, channels, axis=1)
    
    for chn_no,p in enumerate(plots, 1):
        c       = pg.PlotCurveItem(pen=(chn_no,channels * 1.3))
        p.addItem(c)
        curves.append((c, chn_no))
          
    def update():
        nonlocal data

        data[:,1:] = numpy.roll(data[:,1:], 100, axis=0)
            
        for curve,data_index in curves:
            curve.setData(data[:,0],data[:,data_index])

    timer = QTimer()
    timer.timeout.connect(update)
    timer.start(30)
    return app.exec_()   


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    cProfile.run("program()", sort="cumtime")
    #program()
ds是一个QtCore.QDataStream,路径是QPainterPath。然而,我完全不明白>>操作花费这么多时间的原因。因此,我正在寻找一种可能加快渲染速度的方法,并希望坚持使用pyqtgraph,即现在不执行切换,例如vispy

原始functions.py arrayToQPath:

def arrayToQPath(x, y, connect='all'):
    """Convert an array of x,y coordinats to QPainterPath as efficiently as possible.
    The *connect* argument may be 'all', indicating that each point should be
    connected to the next; 'pairs', indicating that each pair of points
    should be connected, or an array of int32 values (0 or 1) indicating
    connections.
    """

    ## Create all vertices in path. The method used below creates a binary format so that all
    ## vertices can be read in at once. This binary format may change in future versions of Qt,
    ## so the original (slower) method is left here for emergencies:
        #path.moveTo(x[0], y[0])
        #if connect == 'all':
            #for i in range(1, y.shape[0]):
                #path.lineTo(x[i], y[i])
        #elif connect == 'pairs':
            #for i in range(1, y.shape[0]):
                #if i%2 == 0:
                    #path.lineTo(x[i], y[i])
                #else:
                    #path.moveTo(x[i], y[i])
        #elif isinstance(connect, np.ndarray):
            #for i in range(1, y.shape[0]):
                #if connect[i] == 1:
                    #path.lineTo(x[i], y[i])
                #else:
                    #path.moveTo(x[i], y[i])
        #else:
            #raise Exception('connect argument must be "all", "pairs", or array')

    ## Speed this up using >> operator
    ## Format is:
    ##    numVerts(i4)   0(i4)
    ##    x(f8)   y(f8)   0(i4)    <-- 0 means this vertex does not connect
    ##    x(f8)   y(f8)   1(i4)    <-- 1 means this vertex connects to the previous vertex
    ##    ...
    ##    0(i4)
    ##
    ## All values are big endian--pack using struct.pack('>d') or struct.pack('>i')

    path = QtGui.QPainterPath()

    #profiler = debug.Profiler()
    n = x.shape[0]
    # create empty array, pad with extra space on either end
    arr = np.empty(n+2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')])
    # write first two integers
    #profiler('allocate empty')
    byteview = arr.view(dtype=np.ubyte)
    byteview[:12] = 0
    byteview.data[12:20] = struct.pack('>ii', n, 0)
    #profiler('pack header')
    # Fill array with vertex values
    arr[1:-1]['x'] = x
    arr[1:-1]['y'] = y

    # decide which points are connected by lines
    if eq(connect, 'all'):
        arr[1:-1]['c'] = 1
    elif eq(connect, 'pairs'):
        arr[1:-1]['c'][::2] = 1
        arr[1:-1]['c'][1::2] = 0
    elif eq(connect, 'finite'):
        arr[1:-1]['c'] = np.isfinite(x) & np.isfinite(y)
    elif isinstance(connect, np.ndarray):
        arr[1:-1]['c'] = connect
    else:
        raise Exception('connect argument must be "all", "pairs", "finite", or array')

    #profiler('fill array')
    # write last 0
    lastInd = 20*(n+1)
    byteview.data[lastInd:lastInd+4] = struct.pack('>i', 0)
    #profiler('footer')
    # create datastream object and stream into path

    ## Avoiding this method because QByteArray(str) leaks memory in PySide
    #buf = QtCore.QByteArray(arr.data[12:lastInd+4])  # I think one unnecessary copy happens here

    path.strn = byteview.data[12:lastInd+4] # make sure data doesn't run away
    try:
        buf = QtCore.QByteArray.fromRawData(path.strn)
    except TypeError:
        buf = QtCore.QByteArray(bytes(path.strn))
    #profiler('create buffer')
    ds = QtCore.QDataStream(buf)

    ds >> path
    #profiler('load')

    return path
结果数据流为1.32秒,映射为0.9秒(path.setElementPositionAt,value)

剖析以下C++片段在我的机器上超过8秒:

#include <QtCore/QDataStream>
#include <QtGui/QPainterPath>

int function2(const int samples)
{
    auto size = 8 + samples * 20 + 4;

    std::vector<char> data(size, 0);

    memcpy(data.data(), &samples, 4);

    QByteArray buf(QByteArray::fromRawData(data.data(), size));
    QDataStream ds(buf);

    float ret;
    for (int counter = 0; counter < samples; counter++)
    {
        int type = 1;
        double x = 0, y = 0;

        ds >> type >> x >> y;
        ret = type + x + y;
    }
    return ret;
}

int main()
{    
    const int samples = 10000;
    const int tries = 10000;
    int ret = 0;

    auto start = std::chrono::high_resolution_clock::now();

    for (auto counter = 0; counter < tries; counter++)
    {
        ret += function2(samples);
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsed = end - start;

    std::cout << "done\n";
    std::cout << "Elapsed time: " << elapsed.count() << " s\n";
    std::cout << ret;

    return 0;
}
#包括
#包括
int函数2(常量int示例)
{
自动大小=8+样本*20+4;
std::矢量数据(大小,0);
memcpy(data.data(),&samples,4);
QByteArray buf(QByteArray::fromRawData(data.data(),size));
QDataStream ds(buf);
浮网;
用于(int计数器=0;计数器<样本;计数器++)
{
int型=1;
双x=0,y=0;
ds>>类型>>x>>y;
ret=类型+x+y;
}
返回ret;
}
int main()
{    
const int samples=10000;
常数=10000;
int-ret=0;
自动启动=标准::时钟::高分辨率时钟::现在();
用于(自动计数器=0;计数器<尝试;计数器++)
{
ret+=函数2(样本);
}
自动结束=标准::时钟::高分辨率时钟::现在();
std::chrono::持续时间=结束-开始;

std::cout最简单的解决方案是激活OpenGL模式,即安装PyOpenGLPyOpenGL accelerate模块并启用OpenGL使用。这样,createPath部分就完全被忽略了。我只是在我的应用程序中添加了以下模块:

try:
    import OpenGL
    pg.setConfigOption('useOpenGL', True)
    pg.setConfigOption('enableExperimental', True)
except Exception as e:
    print(f"Enabling OpenGL failed with {e}. Will result in slow rendering. Try installing PyOpenGL.")

有了它,我的电脑就可以用30000个数据点绘制64条记录道而不费吹灰之力。

您好,我是PyQTraph的维护者;我想感谢您对arrayToQPath的剖析。PySide开发人员表示,他们非常开放,乐于接受性能改进,因此我可以用is v向他们介绍一个具体的示例非常有帮助。很抱歉,我这里没有一个好的解决方法,如果你已经找到了,如果你提交了PR,我们将不胜感激。核心问题是QDataStream&operator>>(QDataStream&stream,QPainterPath&path)基本上迭代所有条目,并执行“ds>>键入>>x>>y;”-这是一个完美的例子,OOP导致了一种非常“数据不友好”的设计:每个条目调用3个不同的运算符,包括多个检查。OGL实现以块方式复制整个数据,效率更高。以后可能会使用readRawData以块方式处理数据。但是,这很可能无法通过代码审查,因为它会破坏QDataStream.TL;DR:的内置安全性使用OGL的用例实际上是一个非常好的主意。如果数据来自同一台机器/内存布局,并且所有内容都是按块处理的,那么就不需要内置的安全性了+一个使用专用的渲染引擎。对不起,我不懂OGL?哦,OGL=OpenGL,我应该明白这一点。不是所有我们绘制的小部件都支持OpenGL u幸运的是,我必须想出另一个解决方案来解决这个问题,或者找到另一个性能提升。随着pyqtgraph 0.12.0和Qt6的采用,我们已经逐步停止使用QGLWidget,而是使用QopenglWidget。我们在Windows上遇到了一个有问题的bug;因此,虽然这个解决方案可以解决一些问题,但它绝对不是意味着总的解决方案。
import timeit
import struct
import numpy as np
from PyQt5 import QtGui,QtCore

no_trys = 1000

def test(pass_data, samples = 10000):
    path = QtGui.QPainterPath()

    n = samples
    # create empty array, pad with extra space on either end
    arr = np.zeros(n+2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')])
    # write first two integers
    byteview = arr.view(dtype=np.ubyte)
    byteview.data[12:20] = struct.pack('>ii', n, 0)

    # write last 0
    lastInd = 20*(n+1)
    # create datastream object and stream into path
    path.strn = byteview.data[12:lastInd+4] # make sure data doesn't run away
    buf = QtCore.QByteArray.fromRawData(path.strn)
    ds = QtCore.QDataStream(buf)

    path.reserve(n)
    if pass_data:
        ds >> path

    def func1():
        nonlocal path

        ds = QtCore.QDataStream(buf)
        ds >> path

    def func2():
        nonlocal path
        values = [(i,i,i) for i in range(samples)]
        map(path.setElementPositionAt, values)

    print(timeit.timeit(func1, number=no_trys))
    print(timeit.timeit(func2, number=no_trys))


test(True)
#include <QtCore/QDataStream>
#include <QtGui/QPainterPath>

int function2(const int samples)
{
    auto size = 8 + samples * 20 + 4;

    std::vector<char> data(size, 0);

    memcpy(data.data(), &samples, 4);

    QByteArray buf(QByteArray::fromRawData(data.data(), size));
    QDataStream ds(buf);

    float ret;
    for (int counter = 0; counter < samples; counter++)
    {
        int type = 1;
        double x = 0, y = 0;

        ds >> type >> x >> y;
        ret = type + x + y;
    }
    return ret;
}

int main()
{    
    const int samples = 10000;
    const int tries = 10000;
    int ret = 0;

    auto start = std::chrono::high_resolution_clock::now();

    for (auto counter = 0; counter < tries; counter++)
    {
        ret += function2(samples);
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsed = end - start;

    std::cout << "done\n";
    std::cout << "Elapsed time: " << elapsed.count() << " s\n";
    std::cout << ret;

    return 0;
}
try:
    import OpenGL
    pg.setConfigOption('useOpenGL', True)
    pg.setConfigOption('enableExperimental', True)
except Exception as e:
    print(f"Enabling OpenGL failed with {e}. Will result in slow rendering. Try installing PyOpenGL.")