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. LineSeries with QAbstractListModel's roles
QtWS25 Last Chance

LineSeries with QAbstractListModel's roles

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
qabstractlistmoqmlchartssplineline
12 Posts 3 Posters 2.1k 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.
  • F Offline
    F Offline
    fcarney
    wrote on 15 Oct 2020, 16:43 last edited by
    #2

    I am not sure of exactly what you need, but would a DelegateModel help with this? I am not sure if the DelegateModel can create the objects SplineSeries requires though.

    C++ is a perfectly valid school of magic.

    T 1 Reply Last reply 15 Oct 2020, 18:07
    0
    • F fcarney
      15 Oct 2020, 16:43

      I am not sure of exactly what you need, but would a DelegateModel help with this? I am not sure if the DelegateModel can create the objects SplineSeries requires though.

      T Offline
      T Offline
      Tyrannic
      wrote on 15 Oct 2020, 18:07 last edited by
      #3

      @fcarney I don't think a DelegateModel would be appropriate for what I'm looking for. Essentially I'm just looking for a way to map chart series to a model's roles and not columns.

      1 Reply Last reply
      0
      • F Offline
        F Offline
        fcarney
        wrote on 15 Oct 2020, 19:12 last edited by fcarney
        #4

        I don't think SplineSeries takes a model. You are right DelegateModel maps a model to a model. You want something that can read a model and instance objects in the SplineSeries:

        import QtQuick 2.12
        import QtQuick.Controls 2.12
        import QtQuick.Window 2.12
        import QtCharts 2.15
        import QtQml.Models 2.12
        
        ApplicationWindow {
            visible: true
            width: 400
            height: 900
            title: qsTr("Dynamic Charts")
        
            ListModel {
                id: listModel
        
                ListElement {xv:0; yv:0}
                ListElement {xv:0.25; yv:0.10}
                ListElement {xv:0.25; yv:0.25}
                ListElement {xv:0.50; yv:0.50}
            }
        
            Column {
                width: parent.width
                ChartView {
                    width: parent.width
                    height: 300
                    antialiasing: true
                    legend.visible: false
        
                    Instantiator {
                        model: listModel
                        onObjectAdded: splineseries.insert(index, object.x, object.y)
                        onObjectRemoved: splineseries.remove(index)
                        QtObject{
                            property real x: xv
                            property real y: yv
                        }
                    }
        
                    SplineSeries {
                        id: splineseries
                    }
                }
            }
        
            //Component.onCompleted: console.log(listModel.count)
        }
        

        I tried a Repeater inside the SplineSeries, but it would not create items for whatever reason. Unsure why. I don't think the SplineSeries liked the Repeater.

        C++ is a perfectly valid school of magic.

        T 1 Reply Last reply 15 Oct 2020, 21:30
        0
        • F fcarney
          15 Oct 2020, 19:12

          I don't think SplineSeries takes a model. You are right DelegateModel maps a model to a model. You want something that can read a model and instance objects in the SplineSeries:

          import QtQuick 2.12
          import QtQuick.Controls 2.12
          import QtQuick.Window 2.12
          import QtCharts 2.15
          import QtQml.Models 2.12
          
          ApplicationWindow {
              visible: true
              width: 400
              height: 900
              title: qsTr("Dynamic Charts")
          
              ListModel {
                  id: listModel
          
                  ListElement {xv:0; yv:0}
                  ListElement {xv:0.25; yv:0.10}
                  ListElement {xv:0.25; yv:0.25}
                  ListElement {xv:0.50; yv:0.50}
              }
          
              Column {
                  width: parent.width
                  ChartView {
                      width: parent.width
                      height: 300
                      antialiasing: true
                      legend.visible: false
          
                      Instantiator {
                          model: listModel
                          onObjectAdded: splineseries.insert(index, object.x, object.y)
                          onObjectRemoved: splineseries.remove(index)
                          QtObject{
                              property real x: xv
                              property real y: yv
                          }
                      }
          
                      SplineSeries {
                          id: splineseries
                      }
                  }
              }
          
              //Component.onCompleted: console.log(listModel.count)
          }
          

          I tried a Repeater inside the SplineSeries, but it would not create items for whatever reason. Unsure why. I don't think the SplineSeries liked the Repeater.

          T Offline
          T Offline
          Tyrannic
          wrote on 15 Oct 2020, 21:30 last edited by Tyrannic
          #5

          @fcarney I did get your example code to work, but expanding that to my problem seems a bit unfeasible. I provided my "trivial" example, but in the real world I have almost 100 roles. It seems that I'd have to create a property in the QtObject that was in your Instantiator for each one of my roles.

          I'm really just looking for something that works somewhat like the example I provided originally, because there's actually a 3d charting QML object that does do it via roles, exactly the way I want, except in 3d and not 2d:

          Scatter3D {
              anchors.fill: parent
                      
              Scatter3DSeries {
                  ItemModelScatterDataProxy {
                      itemModel: itemModel
                              
                      xPosRole: "receivedTime" // Item roles from the original code I provided
                      yPosRole: "val1"
                      zPosRole: "val2"
                  }
              }
          }
          

          I'd really like essentially what is above, but in 2d chart form. I'm not sure why QML provides these objects for their 3d charting library, but not the 2d one.

          1 Reply Last reply
          0
          • G Offline
            G Offline
            GrecKo
            Qt Champions 2018
            wrote on 16 Oct 2020, 07:40 last edited by
            #6

            You correctly identified the problem that VXYModelMapper works with model with columns and your model has multiple roles instead of columns.

            If you don't want to modify your model to also add columns, you could write a proxy model transposing roles to columns as a middle man. For inspiration, the reverse has been done there : https://github.com/KDAB/KDToolBox/tree/master/qt/model_view/sortProxyModel

            T 1 Reply Last reply 16 Oct 2020, 20:42
            0
            • G GrecKo
              16 Oct 2020, 07:40

              You correctly identified the problem that VXYModelMapper works with model with columns and your model has multiple roles instead of columns.

              If you don't want to modify your model to also add columns, you could write a proxy model transposing roles to columns as a middle man. For inspiration, the reverse has been done there : https://github.com/KDAB/KDToolBox/tree/master/qt/model_view/sortProxyModel

              T Offline
              T Offline
              Tyrannic
              wrote on 16 Oct 2020, 20:42 last edited by Tyrannic
              #7

              @GrecKo Thanks GrecKo for the inspiration for that. I created my own QIdentityProxyModel (wasn't sure if I should use that one, or QAbstractProxyModel) as follows:

              class TrivialTableProxyModel: public QIdentityProxyModel
              {
                  Q_OBJECT
                  QML_ELEMENT
              public:
                  TrivialTableProxyModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {};
              
                  virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override {
                      if(!index.isValid()) {
                          return {};
                      }
                      
                      if(role != Qt::DisplayRole) {
                          return {};
                      }
                      qDebug() << "Proxy:" << index << ", Role:" << role;
                      
                      return sourceModel()->data(createIndex(index.row(), 0), index.column() + Qt::UserRole + 1); // Adding Qt::UserRole+1 since that's where my roles start
                  }
              

              And in QML I implemented it as follows:

              ChartView {
                  anchors.fill: parent
                  antialiasing: true
                  legend.visible: true
                  
                  ScatterSeries {
                      name: "Data"
                      axisX: DateTimeAxis { }
                      axisY: ValueAxis { }
              
                      VXYModelMapper {
                          xColumn: 0 // Received time role/column
                          yColumn: 1 // Var1 role/column
                          model: trivialTableProxyModel // Assume this was inserted into the root context
                      }
                  }
              }
              

              It seems like it's much closer (I even got it working with manually setting the VXYModelMapper's rowCount), but when I leave it at the default -1, it seems to endlessly call the data function. I even went through the effort of turning my original model into a QAbstractTableModel, but with the same result. Here's an output of the print statements from the proxy's data function and the model's data function:

              Proxy: QModelIndex(1,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(1,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              Proxy: QModelIndex(1,1,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(1,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              Proxy: QModelIndex(2,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(2,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              Proxy: QModelIndex(2,1,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(2,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              ...
              Proxy: QModelIndex(12065,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(12065,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              Proxy: QModelIndex(12065,1,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(12065,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              Proxy: QModelIndex(12066,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(12066,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              Proxy: QModelIndex(12066,1,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 0
              Model: QModelIndex(12066,0,0x0,HousekeepingTableProxyModel(0x399856f580)), Role: 1
              

              It may be hard to decipher, but the translation is working from roles/columns. I'm just now not sure why VXYModelMapper endlessly calls the data functions with ever increasing row counts. Is this a bug, or did I implement my model incorrectly?

              1 Reply Last reply
              0
              • F Offline
                F Offline
                fcarney
                wrote on 16 Oct 2020, 20:47 last edited by
                #8

                @Tyrannic said in LineSeries with QAbstractListModel's roles:

                ScatterSeries

                I don't see a dataProxy in the ScatterSeries docs like I see in the Scatter3DSeries. I don't see any docs that a ScatterSeries takes a model. How is this supposed to work?

                C++ is a perfectly valid school of magic.

                T 1 Reply Last reply 16 Oct 2020, 20:56
                0
                • F Offline
                  F Offline
                  fcarney
                  wrote on 16 Oct 2020, 20:56 last edited by
                  #9

                  Anyway, I may have a way to do go wide on your data. But I am unsure if this fits your data. I don't have a good feel for what is in your model:

                  import QtQuick 2.12
                  import QtQuick.Controls 2.12
                  import QtQuick.Window 2.12
                  import QtCharts 2.15
                  import QtQml.Models 2.12
                  
                  ApplicationWindow {
                      visible: true
                      width: 400
                      height: 900
                      title: qsTr("Dynamic Charts")
                  
                      ListModel {
                          id: listModel
                  
                          ListElement {xv1:0.00; yv1:0.00; xv2:0.10; yv2:0.10; xv3:0.25; yv3:0.10; xv4:0.10; yv4:0.25}
                          ListElement {xv1:0.50; yv1:0.50; xv2:0.75; yv2:0.50; xv3:0.50; yv3:0.75; xv4:1.00; yv4:1.00}
                      }
                  
                      Column {
                          width: parent.width
                          ChartView {
                              width: parent.width
                              height: 300
                              antialiasing: true
                              legend.visible: false
                  
                              Instantiator {
                                  model: listModel
                                  onObjectAdded: {
                                      for(var subindex=0; subindex<8; ++subindex){
                                          console.log(index*8+subindex)
                                          splineseries.insert(index*8+subindex, object.points[subindex].x, object.points[subindex].y)
                                      }
                                  }
                                  onObjectRemoved: {
                                      for(var subindex=0; subindex<8; ++subindex){
                                          splineseries.remove(index*8+subindex)
                                      }
                                  }
                                  delegate: QtObject{
                                      property var points: {
                                          var arr = []
                  
                                          arr.push(Qt.point(xv1, yv1))
                                          arr.push(Qt.point(xv2, yv2))
                                          arr.push(Qt.point(xv3, yv3))
                                          arr.push(Qt.point(xv4, yv4))
                  
                                          return arr
                                      }
                                  }
                              }
                  
                              SplineSeries {
                                  id: splineseries
                              }
                          }
                      }
                  }
                  

                  C++ is a perfectly valid school of magic.

                  1 Reply Last reply
                  0
                  • F fcarney
                    16 Oct 2020, 20:47

                    @Tyrannic said in LineSeries with QAbstractListModel's roles:

                    ScatterSeries

                    I don't see a dataProxy in the ScatterSeries docs like I see in the Scatter3DSeries. I don't see any docs that a ScatterSeries takes a model. How is this supposed to work?

                    T Offline
                    T Offline
                    Tyrannic
                    wrote on 16 Oct 2020, 20:56 last edited by
                    #10

                    @fcarney Well, that was somewhat the point, was that it was frustrating that the Scatter3dSeries could take a roles as demonstrated by my example code, but not the ScatterSeries. Either way, I'm one step closer to having a solution thanks to @GrecKo, but as illustrated by my last post, VXYModelMapper seems to be misbehaving when rowCount is -1. That or I messed up the Proxy and/or model.

                    1 Reply Last reply
                    0
                    • F Offline
                      F Offline
                      fcarney
                      wrote on 16 Oct 2020, 20:59 last edited by fcarney
                      #11

                      For checking your model put this in your models constructor:

                      new QAbstractItemModelTester(this, QAbstractItemModelTester::FailureReportingMode::Warning, this);
                      

                      This will print out issues it finds. You can look at the source code to that class to see exactly what it is testing in the Qt source.

                      C++ is a perfectly valid school of magic.

                      T 1 Reply Last reply 16 Oct 2020, 21:08
                      0
                      • F fcarney
                        16 Oct 2020, 20:59

                        For checking your model put this in your models constructor:

                        new QAbstractItemModelTester(this, QAbstractItemModelTester::FailureReportingMode::Warning, this);
                        

                        This will print out issues it finds. You can look at the source code to that class to see exactly what it is testing in the Qt source.

                        T Offline
                        T Offline
                        Tyrannic
                        wrote on 16 Oct 2020, 21:08 last edited by
                        #12

                        @fcarney I put the QAbstractItemModelTester in both my model, and my proxy model, and... no errors. I also swapped it to FailureReportingMode::Fatal to double check, and nothing. The endless calls to data from VXYModelMapper continue.

                        1 Reply Last reply
                        0

                        11/12

                        16 Oct 2020, 20:59

                        • Login

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