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.
  • fcarneyF fcarney

    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 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
    • fcarneyF Offline
      fcarneyF Offline
      fcarney
      wrote on 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
      0
      • fcarneyF fcarney

        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 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
        • GrecKoG Offline
          GrecKoG Offline
          GrecKo
          Qt Champions 2018
          wrote on 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
          0
          • GrecKoG GrecKo

            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 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
            • fcarneyF Offline
              fcarneyF Offline
              fcarney
              wrote on 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
              0
              • fcarneyF Offline
                fcarneyF Offline
                fcarney
                wrote on 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
                • fcarneyF fcarney

                  @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 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
                  • fcarneyF Offline
                    fcarneyF Offline
                    fcarney
                    wrote on 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
                    0
                    • fcarneyF fcarney

                      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 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

                      • Login

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