Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QML LineSeries with c++ Vector Data
QtWS25 Last Chance

QML LineSeries with c++ Vector Data

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
lineseriesqmlvector
15 Posts 2 Posters 14.6k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    jars121
    wrote on 3 Dec 2017, 20:42 last edited by
    #1

    Good morning,

    I'm looking to develop a basic real-time LineSeries plot, using the method/implementation defined in the Qt documentation:

    The LineSeries points are added as such:

    XYPoint { x: 0; y: 0 }
    

    I have a c++ class which reads data from a variety of sources, and appends them to a Vector:

    std::vector< int > arr;
    
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);
    //etc.
    

    How can I plot arr data in the QML LineSeries? I'm completely new to plotting with Qt/QML, so I'm not sure if this is even an optimal approach. Performance is the primary consideration in this case, as I'll be deploying on an embedded device with limited processing capabilities.

    Thanks!

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jars121
      wrote on 3 Dec 2017, 22:48 last edited by jars121 12 Mar 2017, 23:41
      #2

      Just thinking out loud, could I perhaps use a Repeater in my QML document, to add XYPoints for each index of the c++ Vector? Pseudo-code below.

       LineSeries {
              name: "LineSeries"
              Repeater {
                  model: MyCppVector.size
                  XYPoint { x: MyCppVector.array[index]; y: index }
              }
          }
      
      1 Reply Last reply
      0
      • 6 Offline
        6 Offline
        6thC
        wrote on 4 Dec 2017, 01:32 last edited by 6thC 12 Apr 2017, 01:33
        #3

        I too use std containers and have my own QML / C++ solution. I may not have the answer but I have mine.
        I use a pointer to the QML LineSeries and do the data gathering, processing and LineSeries updates in C++

        In QML I set my axis:

        ChartView {
        DateTimeAxis {
                       id: valueAxisX
                       format: "m"
                       reverse: true;
                   }
        ValueAxis {
        id: valueAxisY
        min: 0
        max: 100.0
        tickCount: 5
        /reverse: true;
        }
        LineSeries { ....
        

        and if you want performance - you got it - just specify in your series (mine are LineSeries):
        useOpenGL: true (see https://doc.qt.io/qt-5/qabstractseries.html#useOpenGL-prop) - there are some functionality sacrifices using this - but I need speed.

        As far as the data goes, you just need to change that std vec into a QVector<QPointF> and call the QXYSeries replace() https://doc.qt.io/qt-5/qxyseries.html#replace-5 - building your data up first and using replace is fast. I'd recommend not using anything like add - they fire way too much off.

        I use a ptr to the QML object gathered in advance to get access to the series I want.

        J 1 Reply Last reply 4 Dec 2017, 03:17
        0
        • 6 6thC
          4 Dec 2017, 01:32

          I too use std containers and have my own QML / C++ solution. I may not have the answer but I have mine.
          I use a pointer to the QML LineSeries and do the data gathering, processing and LineSeries updates in C++

          In QML I set my axis:

          ChartView {
          DateTimeAxis {
                         id: valueAxisX
                         format: "m"
                         reverse: true;
                     }
          ValueAxis {
          id: valueAxisY
          min: 0
          max: 100.0
          tickCount: 5
          /reverse: true;
          }
          LineSeries { ....
          

          and if you want performance - you got it - just specify in your series (mine are LineSeries):
          useOpenGL: true (see https://doc.qt.io/qt-5/qabstractseries.html#useOpenGL-prop) - there are some functionality sacrifices using this - but I need speed.

          As far as the data goes, you just need to change that std vec into a QVector<QPointF> and call the QXYSeries replace() https://doc.qt.io/qt-5/qxyseries.html#replace-5 - building your data up first and using replace is fast. I'd recommend not using anything like add - they fire way too much off.

          I use a ptr to the QML object gathered in advance to get access to the series I want.

          J Offline
          J Offline
          jars121
          wrote on 4 Dec 2017, 03:17 last edited by
          #4

          @6thC Thank you very much, you've introduced a number of new (to me) elements in your approach. I don't suppose you have an example of how you would set up the LineSeries in c++ and then point to the LineSeries in QML? Similarly, how exactly is the OpenGL declaration made in c++? I understand the use of QXYSeries.replace().

          6 1 Reply Last reply 4 Dec 2017, 04:07
          0
          • J jars121
            4 Dec 2017, 03:17

            @6thC Thank you very much, you've introduced a number of new (to me) elements in your approach. I don't suppose you have an example of how you would set up the LineSeries in c++ and then point to the LineSeries in QML? Similarly, how exactly is the OpenGL declaration made in c++? I understand the use of QXYSeries.replace().

            6 Offline
            6 Offline
            6thC
            wrote on 4 Dec 2017, 04:07 last edited by 6thC 12 Apr 2017, 04:13
            #5

            @jars121
            QML XYSeries gives you
            QML AbstractSeries
            which exposes: https://doc.qt.io/qt-5/qml-qtcharts-abstractseries.html#useOpenGL-prop

            useOpenGL : bool
            

            But you can also in C++ use the QAbstractSeries : useOpenGL https://doc.qt.io/qt-5/qabstractseries.html#useOpenGL-prop with the pointer of your series as such:

            pSeries->setUseOpenGL(true );
            // or disable
            pSeries->setUseOpenGL(false);
            

            I group up my series into charts both the LineSeries and the ChartView through a slot. I use the ChartView to set value dynamic ranges series data.

            I've exposed this ChartPointers instance to the QML Engine as: ChartPointersCpp

            context->setContextProperty(QLatin1Literal("ChartPointersCpp"), &coreEngineInstance->Charts );
            

            Called this instance's addSeries with the chart and series QML instances as such:

            // QML
            ChartView {
                        id: chart;
            ...
            // I've just lazily hardcoded the series instance - I'm sure dynamic is fine too
            LineSeries {id: series1; ...
            LineSeries {id: series2; ...
            LineSeries {id: series3; ...
            ...
            

            I have an event trigger (onMouseclick: on a (custom legend) item to set the series visible: true/false

            function processSeries(series){
            visible = !visible; // toggle
            ... if visible 
            ChartPointersCpp.addSeries(chart, series);
            ... else not visible
            ChartPointersCpp.removeSeries(series); // we already know the chartview ptr from c++ now
            

            I receives this call as:

            ChartPointers::addSeries(QObject* chartView, QLineSeries *pSeries);
            

            Once you have pSeries ... QAbstractSeries (I have a QLineSeries hardcoded) is all yours. Just be careful for nullptrs and the rest as I still let QML be it's parent etc, my c++ doesn't assume there's a valid ptr, if it's invalid it will remove it from the memory container as it was nullptr

            I use the chartView to access chart properties declared by me around dynamic ranges:

            qint64 beginMs = pChartView->property("beginMs").toLongLong();
            

            Think that should be enough to have you having some fun...

            J 1 Reply Last reply 4 Dec 2017, 06:36
            0
            • 6 6thC
              4 Dec 2017, 04:07

              @jars121
              QML XYSeries gives you
              QML AbstractSeries
              which exposes: https://doc.qt.io/qt-5/qml-qtcharts-abstractseries.html#useOpenGL-prop

              useOpenGL : bool
              

              But you can also in C++ use the QAbstractSeries : useOpenGL https://doc.qt.io/qt-5/qabstractseries.html#useOpenGL-prop with the pointer of your series as such:

              pSeries->setUseOpenGL(true );
              // or disable
              pSeries->setUseOpenGL(false);
              

              I group up my series into charts both the LineSeries and the ChartView through a slot. I use the ChartView to set value dynamic ranges series data.

              I've exposed this ChartPointers instance to the QML Engine as: ChartPointersCpp

              context->setContextProperty(QLatin1Literal("ChartPointersCpp"), &coreEngineInstance->Charts );
              

              Called this instance's addSeries with the chart and series QML instances as such:

              // QML
              ChartView {
                          id: chart;
              ...
              // I've just lazily hardcoded the series instance - I'm sure dynamic is fine too
              LineSeries {id: series1; ...
              LineSeries {id: series2; ...
              LineSeries {id: series3; ...
              ...
              

              I have an event trigger (onMouseclick: on a (custom legend) item to set the series visible: true/false

              function processSeries(series){
              visible = !visible; // toggle
              ... if visible 
              ChartPointersCpp.addSeries(chart, series);
              ... else not visible
              ChartPointersCpp.removeSeries(series); // we already know the chartview ptr from c++ now
              

              I receives this call as:

              ChartPointers::addSeries(QObject* chartView, QLineSeries *pSeries);
              

              Once you have pSeries ... QAbstractSeries (I have a QLineSeries hardcoded) is all yours. Just be careful for nullptrs and the rest as I still let QML be it's parent etc, my c++ doesn't assume there's a valid ptr, if it's invalid it will remove it from the memory container as it was nullptr

              I use the chartView to access chart properties declared by me around dynamic ranges:

              qint64 beginMs = pChartView->property("beginMs").toLongLong();
              

              Think that should be enough to have you having some fun...

              J Offline
              J Offline
              jars121
              wrote on 4 Dec 2017, 06:36 last edited by jars121 12 Apr 2017, 09:20
              #6

              @6thC Thanks again, that's given me plenty to work with.

              I'm doing some reading at the moment, and have found that I can't use QtCharts with QGuiApplication, and have to instead use QApplication? I've tried swapping QGuiApplication in my .pro and .cpp files (QT += widgets and #include <QApplication> respectively), which prevents me from using the various QQml classes I'm already using (QQmlApplicationEngine, QQmlEngine, etc.)?

              I must be missing some quite elementary; how can I use both QApplication for QtCharts and QGuiApplication for all my QML-based classes at the same time?

              EDIT: It looks like I wasn't declaring QT += widgets early enough; I've now got a static LineSeries populated within my QML view. Now I'll start working on having c++ build the LineSeries and update/replace the plot in real-time.

              1 Reply Last reply
              0
              • 6 Offline
                6 Offline
                6thC
                wrote on 4 Dec 2017, 23:11 last edited by 6thC 12 Apr 2017, 23:14
                #7

                Sweet, yes, with Charts it's Widget based. My project file has this entry:

                QT += qml         quick         widgets         quickcontrols2         charts
                

                You should be good to go already on the charts end then! Just update the data. When the points have been replaced using:

                void QXYSeries::replace(QVector<QPointF> points)
                

                it Emits

                 QXYSeries::pointsReplaced()
                

                You may need this somewhere - if it's working already I'd call it a win!:

                QT_CHARTS_USE_NAMESPACE
                

                I found most of this from pulling apart and playing with:
                Qml Oscilloscope

                Take a look inside if you are curious. I modified this in a copy to use many more points.
                It was 10 * series, each with 100, 000 points updating with the same random data found in the Oscilloscope example app - all at rates every 16ms!

                And I just have a crappy Intel HD Graphics 530! It pulls ~45 to 60 fps with this going on. Anyhow, good luck, let me know.

                J 1 Reply Last reply 6 Dec 2017, 07:23
                0
                • 6 6thC
                  4 Dec 2017, 23:11

                  Sweet, yes, with Charts it's Widget based. My project file has this entry:

                  QT += qml         quick         widgets         quickcontrols2         charts
                  

                  You should be good to go already on the charts end then! Just update the data. When the points have been replaced using:

                  void QXYSeries::replace(QVector<QPointF> points)
                  

                  it Emits

                   QXYSeries::pointsReplaced()
                  

                  You may need this somewhere - if it's working already I'd call it a win!:

                  QT_CHARTS_USE_NAMESPACE
                  

                  I found most of this from pulling apart and playing with:
                  Qml Oscilloscope

                  Take a look inside if you are curious. I modified this in a copy to use many more points.
                  It was 10 * series, each with 100, 000 points updating with the same random data found in the Oscilloscope example app - all at rates every 16ms!

                  And I just have a crappy Intel HD Graphics 530! It pulls ~45 to 60 fps with this going on. Anyhow, good luck, let me know.

                  J Offline
                  J Offline
                  jars121
                  wrote on 6 Dec 2017, 07:23 last edited by
                  #8

                  @6thC Thanks again for your help!

                  I've poured over the linked QML Oscilloscope example, and am afraid I've confused myself even further. I've just come across to c++ from Python (PyQt and PySide), so the declarations, use of header files, contextProperties, etc. are thoroughly confusing me at this point.

                  Just so I'm clear, do you update the QPointF vector/array from c++? The QML Oscilloscope example has a Timer function in the .qml ChartView file which polls c++ for data, which isn't the approach I'd like to use. As (I believe you've described), I'd like for c++ to collect the data and push it to QML (using the replace() function as you've described).

                  I've included a detailed breakdown of where I'm up to below (irrelevant sections omitted for simplicity).

                  Data.cpp:

                  #include "data.h"
                  #include <QtCharts/QXYSeries"
                  
                  QT_CHARTS_USE_NAMESPACE
                  
                  Q_DECLARE_METATYPE(QAbstractSeries *)
                  Q_DECLARE_METATYPE(QAbstractAxis *)
                  
                  Data::Data() :
                      m_Value(0), m_Index(-1)
                      {
                          qRegisterMetaType<QAbstractSeries*>();
                          qRegisterMetaType<QAbstractAxis*>();
                  
                          this->m_Timer = new QTimer(this);
                          this->m_Timer->setInterval(200);
                          connect(this->m_Timer, &QTimer::timeout, this, &Data::Timeout);
                          this->m_Timer->start();
                      }
                  
                      void Data::dataUpdate(QAbstractSeries *series)
                      {
                          if (series) {
                              QXYSeries *xySeries = static_cast<QXYSeries *>(series);
                              m_Index++;
                              if (m_Index > m_Data.count() - 1)
                                  m_Index = 0;
                  
                              QVector<QPointF> points = m_Data.at(m_Index);
                              xySeries->replace(points);
                          }
                      }
                  
                      void Data::generateData(int value1, int value2)
                      {
                          //Receive the latest data values and append them to m_Data?
                          //Once the vector/array has reached a certain size (e.g. 10),
                          //Append new values to the end and remove initial values so the
                          //Data set 'scrolls' with a fixed size
                      }
                  
                      void DataDemo::Timeout()
                      {
                          int HIGH = 10;
                          int LOW = 0;
                          this->m_Value = rand() % (HIGH - LOW + 1) + LOW;
                          emit ValueChanged();
                          generateData(m_Value);
                      }
                  

                  DataDemo.h:

                  #ifndef DATADEMO_H
                  #define DATADEMO_H
                  
                  #include <QObject>
                  #include <QTimer>
                  #include <QtCore/QObject>
                  #include <QtCharts/QAbstractSeries>
                  
                  QT_BEGIN_NAMESPACE
                  class QQuickView;
                  QT_END_NAMESPACE
                  
                  QT_CHARTS_USE_NAMESPACE
                  
                  class Data : public QObjects
                  {
                      Q_OBJECT
                  public:
                      Data();
                      Q_PROPERTY(int value READ value NOTIFY valueChanged)
                      int value(){return this->m_Value;}
                  
                      //The below was taken from the QML Oscilloscope example
                      //I don't know if it's needed in my case or what it's for
                      explicit Data(QQuickView *appViewer, QObject *parent = 0);
                  
                  signals:
                      void valueChanged();
                  
                  private slots:
                      void Timeout();
                  
                  public slots:
                      void dataUpdate(QAbstractSeries *series);
                      void generateData(int value1, int value2);
                  
                  private:
                      int         m_Value;
                      QTimer    * m_Timer;
                  
                      QQuickView *m_appViewer;
                      QList<QVector<QPointF> > m_Data;
                      int         m_index;
                  };
                  #endif // DATADEMO_H
                  

                  main.cpp:

                  //#include a whole host of items here
                  
                  using namespace std;
                  
                  int main(int argc, char *argv[])
                  {
                      QApplication app(argc, argv);
                      QQmlApplicationEngine engine;
                  
                      engine.rootContext()->setContextProperty(QStringLiteral("Data"), new Data());
                  
                      engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
                      if (engine.rootObjects().isEmpty())
                          return -1;
                  
                      return app.exec();
                  }
                  

                  main.qml:

                  ChartView {
                      id: chartView
                      property bool openGL: true
                      property bool openGLSupported: true
                      antialiasing: true
                      onOpenGLChanged: {
                          if (openGLSupported) {
                              series("signal 1").useOpenGL = openGL;
                          }
                      }
                      Component.onCompleted: {
                          if (!series("signal 1").useOpenGL) {
                              openGLSupported = false
                              openGL = false
                          }
                      }
                      //A couple of ValueAxis elements here
                  
                      LineSeries {
                          id: lineSeries1
                          name: "signal 1"
                          //If I use the below line I get OpenGLFrameBuffer issues for some reason
                          useOpenGL: chartView.openGL
                      }
                  
                      //I don't know if I want to use this Timer function (straight from the Oscilloscope demo)
                      //I would rather the data update be pushed from c++ than requested from QML
                      Timer {
                          id: refreshTimer
                          interval: 200
                          running: true
                          repeat: true
                          onTriggered: {
                              Data.dataUpdate(chartView.series(0));
                          }
                      }
                  }
                  

                  I've cut a considerable amount out of my actual code, and have renamed almost everything to make it easier to follow, so if something doesn't make sense it's because I've done so incorrectly for this post. The application compiles and runs without error (provide the openGL line mentioned above is commented out), but the plot remains empty (the axes, grid, etc. are shown).

                  Where am I going wrong?

                  1 Reply Last reply
                  0
                  • 6 Offline
                    6 Offline
                    6thC
                    wrote on 6 Dec 2017, 10:55 last edited by
                    #9

                    Sorry to have confused you - they've only made that data class and timer so there's a constant supply of new data to show charts off - yes, you do use your data and with signals and slots - only replace the series points when you wish/require.

                    I won't be near a machine for another 12 hours or so but I'll take a look at what you have and see if I can help you out then.

                    Oh and sorry, I must have sent you the wrong example app, the one I had was a pure widget app I abused to make myself, it had a timer and everything but in c++ side.

                    J 1 Reply Last reply 6 Dec 2017, 11:03
                    0
                    • 6 6thC
                      6 Dec 2017, 10:55

                      Sorry to have confused you - they've only made that data class and timer so there's a constant supply of new data to show charts off - yes, you do use your data and with signals and slots - only replace the series points when you wish/require.

                      I won't be near a machine for another 12 hours or so but I'll take a look at what you have and see if I can help you out then.

                      Oh and sorry, I must have sent you the wrong example app, the one I had was a pure widget app I abused to make myself, it had a timer and everything but in c++ side.

                      J Offline
                      J Offline
                      jars121
                      wrote on 6 Dec 2017, 11:03 last edited by
                      #10

                      @6thC That's quite alright! I'm thoroughly confused almost all the time at the moment as I embark on this c++ adventure; your input has been one of the few things I've managed to delve into and understand at least at a high level.

                      If you don't mind have a flick through whenever you get a chance I'd really appreciate it! I'll continue reading for the time being, and will made another attempt tomorrow. Thanks again!

                      1 Reply Last reply
                      0
                      • 6 Offline
                        6 Offline
                        6thC
                        wrote on 6 Dec 2017, 23:32 last edited by
                        #11

                        Bit of a sidetrack, but maybe useful if you follow me this way later on... I've just been testing closing and destroy()ing charts from the QML side. I've come across a nice memory access violation with my design. I should have seen it coming - using pointers to objects that weren't mine.

                        I think my design has it's risks, I have mitigated it by calling my remove method on close just before I call destroy.

                        Realizing this and solving that also exposed a risk in my own c++ design where I would deadlock (certainly) when I go multi-thread - and could hit currently via using a QML created signal.
                        I was emit'ing inside c++ but that emit was inside iterations of a container member protected by a mutex / std::lock_guard. The slot connected to that emit'd signal sent it's own signal which a different slot but in the original class used a lock over that same member container.

                        Anyhow. I'm learning as I go - I'm kind of apologizing if my way isn't the ideal way - it is my way and my way is working for me currently. Whether it's the most efficient way... I always like to listen to others with experience.

                        1 Reply Last reply
                        0
                        • 6 Offline
                          6 Offline
                          6thC
                          wrote on 6 Dec 2017, 23:43 last edited by
                          #12

                          To fix what you have - as long as in c++ somewhere you use charts you have:

                          #include <QtCharts>
                          // I think that macro earlier just does the namespacing, I just removed the macro in my own application for this myself...
                          using namespace QtCharts; 
                          

                          Really, the only changes you need to make is in your ChartView (QML).

                          • give it dimensions somehow
                            height & width / anchors.fill: parent; / etc
                          • give it Axes ( axis(s) )
                          DateTimeAxis {
                              id: valueAxisX
                              format: "s"
                              reverse: true;
                          }
                          ValueAxis {
                              id: valueAxisY // vertical axis
                              min: 0
                              max: 100.0
                              tickCount: 5
                              //reverse: true;
                          }
                          
                          • in your LineSeries - assign it those axes:
                          axisX: valueAxisX; axisY : valueAxisY ;
                          
                          1 Reply Last reply
                          0
                          • 6 Offline
                            6 Offline
                            6thC
                            wrote on 6 Dec 2017, 23:53 last edited by
                            #13

                            As far as OpenGL issues go... are you setting anything OpenGL manually? Setting the QSurfaceFormat yourself?

                            Cause I had support raise this: https://bugreports.qt.io/browse/QTBUG-63807 - should be fixed now in 5.9.3

                            Let me know - hopefully now you can ditch all timer references and get to pumping that chart series with data.

                            1 Reply Last reply
                            0
                            • J Offline
                              J Offline
                              jars121
                              wrote on 8 Dec 2017, 06:16 last edited by
                              #14

                              I'm afraid I'm getting progressively more lost the longer I attempt this. I'm reading through the QML Oscilloscope example for the 10th time, and I just can't understand how to apply that to my function. I.e. pushing data to the chart rather than QML requesting data.

                              I'll continue reading and attempting, and will hopefully post back here with some updates/revised code of the coming couple of days.

                              1 Reply Last reply
                              0
                              • 6 Offline
                                6 Offline
                                6thC
                                wrote on 15 Dec 2017, 07:27 last edited by 6thC
                                #15

                                Oh hey. Didn't see you were still stuck.

                                So if you've QML declared your (LineSeries, ScatterSeries, and SplineSeries) - in C++ you'd have a slot (or Q_INVOKABLE) receive the object as a QXYSeries* ptr.

                                Once you have access to a QXYSeries object: (you have a pointer to your series and you know which c++ std::vector matches what series etc) the only work you have to do is reformat your std::vector into a QVector<QPointF> before the replace() call of the QLineSeries*

                                The LineSeries will do the work to repaint.
                                I guess it signals: void QXYSeries::pointsReplaced() which kicks of some ChartView dirty or repaint request. Anyhow, it's free to us.

                                1 Reply Last reply
                                1

                                • Login

                                • Login or register to search.
                                • First post
                                  Last post
                                0
                                • Categories
                                • Recent
                                • Tags
                                • Popular
                                • Users
                                • Groups
                                • Search
                                • Get Qt Extensions
                                • Unsolved