Multiple real time plots/charts



  • Hey guys,

    Since QT uses a single thread for the GUI I'm experiencing, as expected, low performance when displaying 8 and more charts fed with real time data.
    I've tried the QCharts, qcustomplot, qwtPlot. All with and without openGL enabled.
    So far the best performance comes with qcustomplot.

    What would you suggest me to do, to display 10-20 real time charts without performance drop?
    Fork a single chart app and use ipc for data exchange?

    Best regards
    framet


  • Moderators

    @framet How often do you update all these charts?



  • @jsulm The real time data comes in over UDP at about 50 Hz in a separate thread. Then the data will be distributed with a QTimer with 20 HZ.



  • using QChart, how do you update real time data?

    if you receive the "8 and more" data point at the same time (or at least synchronously) you can subclass QAbstractTableModel and update all the values in a signle operation and cause a single repaint of the chart itself (using a model mapper as bridge between the model and the chart)



  • @VRonin I have "8 and more" charts simultaneously open and not one chart with 8 and more data points. Each chart does not have more than 3 data point series. As I understand the QAbstractTableModel is only for one single chart correct? So this probably wont safe me the majority of the performance but is still a good point for QCharts.



  • @framet said in Multiple real time plots/charts:

    QAbstractTableModel is only for one single chart correct?

    Nope. actually it QAbstractTableModel is perfect for when you have more than 1 chart.

    How did you try to update the charts before?



  • @VRonin I'm currently not in the office so I cannot check to be 100% but i think i just added the to the respective QLineSeries.



  • @VRonin said in Multiple real time plots/charts:

    Nope. actually it QAbstractTableModel is perfect for when you have more than 1 chart.

    hey @VRonin im currently working on a test with QAbstractTableModel.
    I have a model with 100 rows and 4 columns. If new values would overflow the memory a just start at row zero again.
    The problem what I have and I can't find any solution for it is that when I update a specific row and emit dataChanged(topLeft,bottomRight) with the updated values QChart updates/repaints the hole model which leads to flickering when you go above 10 HZ.

    Is there a way to reimplement this QChart behavior or is there a flag to set?

    Cheers
    framet

    Edit: here is an example with 2 HZ https://imgur.com/a/DYR7L

    Edit 2: just found out with linesSeries.setUseOpenGL() decreases the flickering



  • Wow, it's unbelievably slow... I'll prepare a minimal example and see if I get the same



  • Below are two tests with TableModel (100,4)

    Both tests run with 20 HZ
    With OpenGL on LineSeries enabled

    Test 1) Single chart https://imgur.com/a/Sfe20
    Test 2) 6 charts http://imgur.com/a/Fpicu

    With OpenGL on LineSeries disabled
    Test 3) 6 charts http://imgur.com/a/mnFag

    As you can see the problem of QT getting slower with more charts relaying on the same Model still exists.

    Edit: just to clarify YES the test before was with 2 HZ and those tests here are with 20 HZ :)



  • Tryed:

    #include <QWidget>
    #include <QTimer>
    #include <QStandardItemModel>
    #include <QtCharts/QChart>
    #include <QtCharts/QChartView>
    #include <QGridLayout>
    #include <QtCharts/QVXYModelMapper>
    #include <QtCharts/QLineSeries>
    #include <cmath>
    
    QT_CHARTS_USE_NAMESPACE
    
    class TestChart : public QWidget
    {
        Q_OBJECT
            Q_DISABLE_COPY(TestChart)
    public:
        explicit TestChart(QWidget *parent = Q_NULLPTR)
            :QWidget(parent)
            , maixLength(360)
            , frequency(20.0)
            , shift(0.0)
            , currentCounter(0.0)
            , pi(std::acos(-1))
        {
            m_model = new QStandardItemModel(this);
            m_model->insertColumns(0, 3);
            //m_model->insertRows(0, maixLength);
    
            m_updateTimer = new QTimer(this);
            m_updateTimer->setInterval(1000.0 / frequency);
            connect(m_updateTimer, &QTimer::timeout, this, &TestChart::addSeriesData);
    
            QGridLayout* mainLay = new QGridLayout(this);
    
            
    
            for (int i = 0; i < 15; ++i) {
                QLineSeries *sineSeries = new QLineSeries;
                sineSeries->setName("Sine Wave");
                QVXYModelMapper *mapper = new QVXYModelMapper(this);
                mapper->setXColumn(0);
                mapper->setYColumn(1);
                mapper->setSeries(sineSeries);
                mapper->setModel(m_model);
                sineSeries->setColor(Qt::red);
    
                QLineSeries *cosineSeries = new QLineSeries;
                cosineSeries->setName("Cosine Wave");
                mapper = new QVXYModelMapper(this);
                mapper->setXColumn(0);
                mapper->setYColumn(2);
                mapper->setSeries(cosineSeries);
                mapper->setModel(m_model);
                cosineSeries->setColor(Qt::blue);
    
                QChart *chart = new QChart();
                chart->legend()->hide();
                chart->addSeries(sineSeries);
                chart->addSeries(cosineSeries);
                chart->createDefaultAxes();
                chart->axisX(sineSeries)->setMin(0);
                chart->axisX(sineSeries)->setMax(maixLength);
                chart->axisX(cosineSeries)->setMin(0);
                chart->axisX(cosineSeries)->setMax(maixLength);
                chart->axisY(sineSeries)->setMin(-1);
                chart->axisY(sineSeries)->setMax(1);
                chart->axisY(cosineSeries)->setMin(-1);
                chart->axisY(cosineSeries)->setMax(1);
    
                chart->setTitle("Simple line chart example");
                QChartView *chartView = new QChartView(chart);
                chartView->setRenderHint(QPainter::Antialiasing);
                mainLay->addWidget(chartView,i/5,i%5 );
            }
    
            m_updateTimer->start();
        }
    private slots:
        void addSeriesData()
        {
            if (m_model->rowCount() <= currentCounter)
                m_model->insertRows(m_model->rowCount(), currentCounter - m_model->rowCount() + 1);
            m_model->setData(m_model->index(currentCounter, 0), currentCounter);
            m_model->setData(m_model->index(currentCounter, 1), std::sin((shift+currentCounter)*pi / 180.0));
            m_model->setData(m_model->index(currentCounter, 2), std::cos((shift + currentCounter)*pi / 180.0));
            currentCounter += 1.0;
            if (currentCounter >= maixLength) {
                currentCounter = 0.0;
                shift += 90;
            }
        }
    
    private:
        const double pi;
        double shift;
        double currentCounter;
        double frequency;
        QTimer* m_updateTimer;
        QAbstractItemModel* m_model;
        const int maixLength;
    };
    

    15 charts, you are right, 20Hz it's not great even if in release mode is better.



  • Do you have any idea how to get 15 charts without severe performance issues?
    Except outsourcing single Charts to seperate Qt-Applications and send data over ipc or something in this direction.



  • I think yo have to decouple the data arrival frequency to the frequency you send the data to the chart.

    In the QAbstractTableModel instead of emitting dataChanged() directly, every time a new datapoint arrives (re)start a signle shot timer that emits the signal. I'll try implementing it and update this post

    EDIT:

    My method increased but not to an acceptable level. I have to give up



  • @VRonin thank you for your time and effort!!!

    I still have some ideas left. Will post it here when one gives an acceptable result.

    cheers!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.