Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Call for Presentations - Qt World Summit

    Solved displaying a C++ QChart in a QML ChartView delegate

    General and Desktop
    2
    6
    1574
    Loading More Posts
    • 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.
    • devDawg
      devDawg last edited by

      Hello all,

      I am looking to visually display several real-time QCharts that are updated with simulation data. I should have no problem with this portion; what I am confused about is the C++/QML interface.

      Disclaimer: I am using Qt 5.9.3 for a specific device.

      I have read through this LineChart example: https://doc.qt.io/qt-5.11/qtcharts-linechart-example.html

      And also the ChartView documentation: https://doc.qt.io/qt-5.11/qml-qtcharts-chartview.html

      My current approach is to use the following class as a context property:

      #include <QtCharts>
      #include <QLineSeries>
      #include <QProcess>
      #include <QQmlComponent>
      #include <QList>
      #include <QStringList>
      #include <QString>
      
      /*This class will be responsible for one specific module. Depending on the page, this model will contain a select number of params.
       * For example, on the summary page, we only want a brief overview of Priority 1 measurements, so the data models will be less compact.
       * Whereas on the following pages that detail full modules, the models will be larger.
       */
      
      
      class DataModel: public QObject
      {
      
          friend class QAbstractListModel;
      
          Q_OBJECT
      
      
      public:
          explicit DataModel(QObject * parent = nullptr);
          explicit DataModel(const QString file); //this is our main constructor here.
          explicit DataModel(DataModel&&); //move constructor
          explicit DataModel(const DataModel&); //copy constructor
      
          DataModel& operator=(const DataModel&);
          DataModel& operator=(DataModel&&);
      
          Q_INVOKABLE QString getTextBodyAt(int i);
          Q_INVOKABLE DataModule *getModuleAt(int i);
          Q_INVOKABLE int getModulesLength() { return modules.length(); }
          Q_INVOKABLE int getGraphsLength() { return graphs.length(); }
          Q_INVOKABLE DataPiece *getDataAt(int index) const;
          Q_INVOKABLE QLineSeries* getSeriesAt(int index) const {return series.at(index);}
          Q_INVOKABLE int getDataCount();
      
          int timeToSeconds(QTime* time) const;
          void addModule(DataModule* mod_);
          void addDataGraph(DataPiece* piece);
          void replaceDataGraph(DataPiece* piece, int index); //replaces the graph at the given index with a new graph for the supplied DataPiece.
          void removeDataGraphAt(int i); //removes the graph at the given index.
          const QVector<DataModule*> getModel() const;  
          const QVector<DataPiece*> getGraphs() {return graphs;}
          const QVector<QLineSeries*> getSeries() {return series;}
          const QVector<QTime*> getTimes() {return timers;}
      
          ~DataModel();
      signals:
          void pieceChanged(DataPiece*); //this signal might not be used anywhere.
          void pieceAdded(DataPiece*);
          void pieceReplaced(DataPiece*);
      
      public slots:
          void onClose();
          void on_pieceAdded(DataPiece *piece);
          void on_pieceReplaced(DataPiece *piece);
      
      private:
          QVector<DataModule*> modules; //separates Modules into Rectangles. Spacing modifiable.
          QVector<DataPiece*> graphs; //for every DataPiece, we will also have a QLineSeries and a QTimer.
          QVector<QLineSeries*> series;
          QVector<QTime*> timers;
      
      
      };
      

      Allowing me to import QLineSeries into my ListView of ChartViews here:

      //this will display our charts.
                          ListView {
                              objectName: "chartListView"
                              id: chartListView
                              x: 0
                              y: 130
                              width: 350
                              height: 350
      
                              anchors.top: controlRect.bottom
                              orientation: Qt.Vertical
      
                              /*
                                  Procedure:
                                  -load a DataPiece in to GraphModel. (name, units, value, maximum, minimum)
                                  -Set up ChartView & LineSeries according to that DataPiece.
                               */
      
                              model: dataModel.getGraphsLength()
      
                              delegate: ChartView {
      
                                  objectName: "chartView"
                                  width: 350
                                  height: 350
                                  title: graphModel.getDataAt(index).getModuleName() + " " + graphModel.getDataAt(index).getName() + " vs. time"
                                  property LineSeries imported_data
                                  //data:
      
                                  //need to import our data from dataModel. This is on the to-do list.
                              }
                          }
      

      -Is this a good approach? I am unsure if, in my context property class, if I should have a QVector of QCharts, or of QLineSeries.

      -In addition, I noticed in QML that ChartView has a property called "data", to which the ChartView documentation makes zero mention. Is this something I could use to import data to my delegate? If so, what type does "data" want? (LineSeries?)

      -Given that my axes will remain static for a given ChartView, I am thinking that these should be set up in my QML delegate. Is my thinking correct on this?

      Note: I should add that it is intended for the user to be able to add or remove graphs from the ListView of ChartViews.

      I apologize for the load of questions, I am just sort of in the dark as to how I should do this the proper way. The Qt documentation is lacking on this topic.

      Thanks in advance.

      Anything worthwhile is never achieved easily.

      1 Reply Last reply Reply Quote 0
      • devDawg
        devDawg last edited by

        Still actively working to make this thing work. I would appreciate any comments on what I am trying to do, and if there is an easier or better way to do it.

        Thanks

        Anything worthwhile is never achieved easily.

        1 Reply Last reply Reply Quote 0
        • devDawg
          devDawg last edited by

          Okay, so I have completely set up my data + signal handlers for new data in C++.

          My current problem is providing the correct format to the delegate of my ListView that will hold my dynamic set of ChartViews.

          Here is the .qml code:

          ListView {
                                  objectName: "chartListView"
                                  id: chartListView
                                  x: 0
                                  y: 130
                                  width: 350
                                  height: 350
          
                                  anchors.top: controlRect.bottom
                                  orientation: Qt.Vertical
          
                                  /*
                                      Procedure:
                                      -load a DataPiece in to GraphModel. (name, units, value, maximum, minimum)
                                      -Set up ChartView & LineSeries according to that DataPiece.
                                   */
          
                                  model: dataModel.getGraphsLength()
                                  delegate: Component {
                                          ChartView { data: dataModel.getChartAt(index) }
          
                                  } //data handling is done in C++.
          
                              }
          

          dataModel is a C++ class that I have registered as a context property.
          The error message I am getting from QML is : Error: Unknown method return type: QChart*. It points to where I am trying to set my ListView delegate.

          datamodel.h:

          class DataModel: public QObject
          {
          
              friend class QAbstractListModel;
          
              Q_OBJECT
          
          
          public:
              explicit DataModel(QObject * parent = nullptr);
              explicit DataModel(const QString file); //this is our main constructor here.
              explicit DataModel(DataModel&&); //move constructor
              explicit DataModel(const DataModel&); //copy constructor
          
              DataModel& operator=(const DataModel&);
              DataModel& operator=(DataModel&&);
          
              Q_INVOKABLE QString getTextBodyAt(int i);
              Q_INVOKABLE DataModule *getModuleAt(int i);
              Q_INVOKABLE int getModulesLength() { return modules.length(); }
              Q_INVOKABLE int getGraphsLength() { return graphs.length(); }
              Q_INVOKABLE DataPiece *getDataAt(int index) const;
              Q_INVOKABLE QChart* getChartAt(int index) const {return charts.at(index)->chart();}
              Q_INVOKABLE int getDataCount();
          
              //depending on the needed accuracy, we will have to look at which data type is needed. Probably won't end up being an int in all likelihood.
              Q_INVOKABLE int getCurrentTimeAt(int index);
          
              long timeToSeconds(QTime* time) const;
              void addModule(DataModule* mod_);
              void addDataGraph(DataPiece* piece);
              void replaceDataGraph(DataPiece* piece, int index); //replaces the graph at the given index with a new graph for the supplied DataPiece.
              void removeDataGraphAt(int i); //removes the graph at the given index.
              const QVector<DataModule*> getModel() const;  
              const QVector<DataPiece*> getGraphs() {return graphs;}
              const QVector<QTime*> getTimes() {return timers;}
          
              ~DataModel();
          signals:
              void pieceChanged(DataPiece*); //this signal might not be used anywhere.
              void pieceAdded(DataPiece*);
              void pieceReplaced(DataPiece*);
          
          public slots:
              void onClose();
              void on_pieceAdded(DataPiece *piece);
              void on_pieceReplaced(DataPiece *piece);
              void on_processError(QProcess::ProcessError error);
              void on_newValue(QString val);
          
          private:
              QVector<DataModule*> modules; //separates Modules into Rectangles. Spacing modifiable.
              QVector<DataPiece*> graphs; //for every DataPiece, we will also have a QLineSeries and a QTimer.
              QVector<QChartView*> charts;
              QVector<QTime*> timers;
          
          
          };
          

          So, should I be setting the delegate to be of type ChartView or Component? If I were to set it as a Component, how would I go about assigning its visual content to be my C++ ChartView at the index?

          Anything worthwhile is never achieved easily.

          1 Reply Last reply Reply Quote 0
          • SGaist
            SGaist Lifetime Qt Champion last edited by

            Hi,

            First thing: QObject based class are not copiable.

            QChart is a widget, so if you want to use it from QtQuick, you should take a look at KDAB's Declarative Widgets module

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            devDawg 1 Reply Last reply Reply Quote 2
            • devDawg
              devDawg @SGaist last edited by

              @SGaist Sounds good. After wrestling with the QtCharts API since early this morning, I am kicking it to the curb and going with QCustomPlot. Seems to be a much cleaner, more efficient, and easier to use. I can't imagine I will have the same problems with QCustomPlot as I was with QtCharts.

              Nonetheless, I appreciate the response!

              Anything worthwhile is never achieved easily.

              1 Reply Last reply Reply Quote 0
              • SGaist
                SGaist Lifetime Qt Champion last edited by

                AFAIK, lots of people are satisfied with QCustomPlot.

                Depending on what you want/need, Qwt might also be of interest.

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                1 Reply Last reply Reply Quote 1
                • First post
                  Last post