Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Is there a way to make QML use 2D data model ?
Qt 6.12 LTS is out! See what's new in the release blog

Is there a way to make QML use 2D data model ?

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
11 Posts 3 Posters 265 Views 1 Watching
  • 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.
  • GilboonetG Offline
    GilboonetG Offline
    Gilboonet
    wrote last edited by Gilboonet
    #1

    Hello, I'm currently using QML and C++ for an application that uses QtQuick3D and it's going really nicely. My problem is that I need to feed QML with data containing a field with more than one row and it doesn't work. I'm using a workaround by putting those rows into a string and using component.onCompleted on the QML side to unflatten the data and dynamically create the list model. But i'm wondering whether there is a way to use another class than QAbstractListModel that handles data fields having more than one value, maybe QAbstractTableModel ? But I'm afraid that there's no datamodel object that I can use for my needs (creating a composite shape, having a parent polygon that can be moved/rotated and children lines that can be hovered/clicked). Is there a demo application using QAbtractTableModel or something alike ? I read somewhere that 2D data model is one of the weaknesses of Qt/QML, so I'm not expecting that it's possible.

    here's my code (QML) : "nested" datamodels gets a string and unflat them using js function creeLignes() from their Component.onCompleted

                        Repeater { // UNE PIECE COMPLETE
                            model: pieces
                            delegate : Shape {
                                containsMode: Shape.FillContains
                                DragHandler {cursorShape: Qt.ClosedHandCursor}
                                ShapePath { // POLYGONE FORMANT LA PIECE
                                    fillRule: ShapePath.WindingFill
                                    fillColor: iToC[coul]
                                    strokeWidth: -1
                                    PathSvg { path: contour }
                                }
    
                                property real tmpPosX : posX
                                property real tmpPosY : posY
                                property real tmpAngle : angle
    
                                x: tmpPosX
                                y: tmpPosY
                                rotation: tmpAngle
    
                                onXChanged: {
                                    pieces.setPosX(index, x);
                                }
                                onYChanged: {
                                    pieces.setPosY(index, y);
                                }
                                onRotationChanged: {
                                    pieces.setAngle(index, rotation);
                                }
    
                                Text { // NUMERO DE LA PIECE (aka n° de sa première facette)
                                    font.pointSize: 10
                                    color: "black"
                                    text: num
                                    horizontalAlignment: Qt.AlignHCenter
                                    verticalAlignment: Qt.AlignVCenter
                                    x: centre.x - (contentWidth /2)
                                    y: centre.y - (contentHeight /2)
                                }
                                Repeater { // CHAQUE NUMERO D'ARETE (ENTRE 2 FACETTES)
                                    model: ListModel {}
                                    Text {
                                        text: model.txt
                                        font.pointSize: 6
                                        horizontalAlignment: Qt.AlignHCenter
                                        verticalAlignment: Qt.AlignVCenter
                                        color: "#1500ff"
                                        transform: [
                                            Translate { x: 0; y: -7 },
                                            Rotation { angle: 180+model.a },
                                            Translate { x: model.x; y: model.y }
                                        ]
                                    }
                                    Component.onCompleted: {
                                        this.model.clear();
                                        var txt = nTxt.split(';');
                                        txt = txt.filter(el=>el);
                                        for (var i in txt) {
                                            var el = txt[i].split(",").map(Number);
                                            this.model.append({"txt": el[0], "x": el[1], "y": el[2], "a": el[3]});
                                        }
                                    }
                                }
    
                                function creeLignes (lignes, liste) {
                                    liste.clear();
                                    const d = 0.25;
                                    var lpli = lignes.split(";");
                                    lpli= lpli.filter((el) => el);
                                    for (var i in lpli) {
                                        var p = lpli[i].split(" ");
                                        p = p.map(Number);
                                        var x1 = p[0];
                                        var y1 = p[1];
                                        var x2 = p[2];
                                        var y2 = p[3];
                                        var f1 = p[4];
                                        var f2 = p[5];
                                        if (x1 > x2) {
                                            [x2, x1] = [x1, x2];
                                            [y2, y1] = [y1, y2];
                                        }
                                        if (y1 < y2) {
                                            liste.append({
                                                "x1": x1-d, "y1": y1+d,
                                                "x2": x1+d, "y2": y1-d,
                                                "x3": x2+d, "y3": y2-d,
                                                "x4": x2-d, "y4": y2+d,
                                                "f1": f1, "f2": f2
                                            });
                                        } else {
                                            liste.append({
                                                "x1": x1-d, "y1": y1-d,
                                                "x2": x1+d, "y2": y1+d,
                                                "x3": x2+d, "y3": y2+d,
                                                "x4": x2-d, "y4": y2-d,
                                                "f1": f1, "f2": f2
                                            });
                                        }
                                    }
                                }
    
                                Repeater { // CHAQUE LIGNE DE CONTOUR
                                    model: ListModel {id: cliste}
                                    Shape {
                                        containsMode: Shape.FillContains
                                        HoverHandler {
                                            id: mouseCont
                                            cursorShape: Qt.CrossCursor
                                        }
                                        property int f1: model.f1
                                        property int f2: model.f2
                                        property var c: mouseCont.hovered ? "yellow" : "black"
                                        ShapePath {
                                            strokeWidth: 0.25
                                            strokeColor: c
                                            fillColor: c
                                            startX: model.x1
                                            startY: model.y1
                                            PathPolyline {
                                                path: [
                                                    Qt.point(model.x2, model.y2),
                                                    Qt.point(model.x3, model.y3),
                                                    Qt.point(model.x4, model.y4),
                                                    Qt.point(model.x1, model.y1),
                                                ]
                                            }
                                            pathHints: ShapePath.PathSolid || ShapePath.PathLinear || ShapePath.PathConvex;
                                        }
                                        TapHandler {
                                            onTapped: {
                                                faces.attache(num, f1, f2);
                                            }
                                        }
                                    }
                                    Component.onCompleted: creeLignes(lcont, cliste)
                                }
    
                                Repeater { // CHAQUE LIGNE DE PLI
                                    model: ListModel {id: pliste}
                                    Shape {
                                        containsMode: Shape.FillContains
                                        HoverHandler {
                                            id: mousePlis
                                            cursorShape: Qt.CrossCursor
                                        }
                                        property int f1: model.f1
                                        property int f2: model.f2
                                        property var c: mousePlis.hovered ? "red" : "white"
                                        ShapePath {
                                            strokeWidth: 0.25
                                            strokeColor: c
                                            fillColor: c
                                            startX: model.x1
                                            startY: model.y1
                                            PathPolyline {
                                                path: [
                                                    Qt.point(model.x2, model.y2),
                                                    Qt.point(model.x3, model.y3),
                                                    Qt.point(model.x4, model.y4),
                                                    Qt.point(model.x1, model.y1),
                                                ]
                                            }
                                            pathHints: ShapePath.PathSolid || ShapePath.PathLinear || ShapePath.PathConvex;
                                        }
                                        TapHandler {
                                            onTapped: {
                                                faces.detache(num, f1, f2);
                                            }
                                        }
                                    }
                                    Component.onCompleted: creeLignes(plis, pliste)
                                }
    
                                WheelHandler { // ALT + roulette pour tourner la pièce
                                    acceptedModifiers: Qt.AltModifier
                                    property: "rotation"
                                    //targetTransformAroundCursor: false
                                    rotationScale: 0.5
                                }
                            }
                        }
                        WheelHandler { // roulette pour zoomer la vue
                            //acceptedModifiers: Qt.ControlModifier
                            property: "scale"
                        }
                    }
    
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote last edited by
      #2

      Hi,

      Are you looking for something like described in the QAbstractItemModel Subclass in the Using C++ Models with Qt Quick Views chapter of Qt's documentation ?

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

      GilboonetG 1 Reply Last reply
      1
      • SGaistS SGaist

        Hi,

        Are you looking for something like described in the QAbstractItemModel Subclass in the Using C++ Models with Qt Quick Views chapter of Qt's documentation ?

        GilboonetG Offline
        GilboonetG Offline
        Gilboonet
        wrote last edited by
        #3

        @SGaist Thank you, I'm going to try that as replacement of QAbstractListModel, I've found an example QML project using that class, apparently there's need to use role. I will post my progress.

        1 Reply Last reply
        0
        • GilboonetG Offline
          GilboonetG Offline
          Gilboonet
          wrote last edited by Gilboonet
          #4

          I'm trying to use QRangeModel as it seems to be a very neat feature.

          I start a new QML Project, add a C++ class to it, instance my class on the main.cpp main() to create my data, create a QRangeModel using the data, add a property with that QRangeModel, that I finally use as model on my QML file.

          For the moment I can only see on the QML side the QRangeModel as a QQmlListModel (from console.log on Component.onCompleted), but I don't see how to use its data.

          Here's my code :
          main.cpp

          #include <QGuiApplication>
          #include "piece.h"
          #include <QRangeModel>
          #include <QQmlApplicationEngine>
          #include <QVariant>
          
          int main(int argc, char *argv[])
          {
              QGuiApplication app(argc, argv);
          
              QList<Piece> mesPieces;
              mesPieces.append(Piece(20, 20));
              mesPieces.append(Piece(60, 0));
              QRangeModel modele(&mesPieces);
          
              QQmlApplicationEngine engine;
              engine.setInitialProperties({
                  {"monModele", QVariant::fromValue(&modele)}
              });
          
              QObject::connect(
                  &engine,
                  &QQmlApplicationEngine::objectCreationFailed,
                  &app,
                  []() { QCoreApplication::exit(-1); },
                  Qt::QueuedConnection);
              engine.loadFromModule("testDeleg2", "Main");
          
              return QGuiApplication::exec();
          }
          

          piece.h (class header)

          #ifndef PIECE_H
          #define PIECE_H
          
          #include <QtTypes>
          
          class Piece
          {
          public:
              qreal x, y;
              Piece() {};
              Piece(qreal px, qreal py) : x(px), y(py) {}
          
              void setX(const qreal newX) {m_x = newX;}
              void setY(const qreal newY) {m_y = newY;}
          
          private:
              qreal m_x;
              qreal m_y;
          };
          
          #endif // PIECE_H
          

          QML

          import QtQuick
          import QtQuick.Controls.Basic
          import QtQuick.Shapes
          
          ApplicationWindow {
              id: window
              width: 640
              height: 480
              minimumWidth: 200
              minimumHeight: 250
              visible: true
              title: qsTr("test QRangeModel")
          
              /*ListModel {
                  id: monModele
                  ListElement { x: 20 ; y: 20 }
                  ListElement { x: 60 ; y: 0 }
              }*/
          
              required property AbstractItemModel monModele
          
              Repeater {
                  model : monModele
                  Shape {
                      anchors.fill: parent
          
                  ShapePath {
                          strokeColor: "yellow"
                          fillColor: "yellow"
                          strokeWidth: 1
                          startX: model.x
                          startY: model.y
                          PathLine { relativeX: 0; relativeY: 50 }
                          PathLine { relativeX: 50; relativeY: 0 }
                      }
                  }
                  Component.onCompleted: {
                      console.log(monModele);
                  }
              }
          }
          

          The ListModels contains what the QRangeModel is supposed to have.
          Maybe do I need to add getters ?

          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote last edited by
            #5

            I haven't used it in such a fashion yet but I think what you want is in the Structs and gadgets as rows chapter In QRangeModel's documentation.

            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
            1
            • GilboonetG Offline
              GilboonetG Offline
              Gilboonet
              wrote last edited by Gilboonet
              #6

              I changed my class, so now it's a Q_GADGET, having properties with getters (READ x and READ y), but even with Q_INVOKABLE, I can't use any of them on the QML side. When I run on debug with a breakpoint and inspect the QRangeModel, it does not seem to have the values, no children. I'm certainly missing something.

              1 Reply Last reply
              0
              • GilboonetG Offline
                GilboonetG Offline
                Gilboonet
                wrote last edited by
                #7

                To get my model data from QML, i needed to use data, giving it an index by using its index function and the Qt.DisplayRole.

                                startX: monModele.data(monModele.index(index,0), Qt.DisplayRole)
                                startY: monModele.data(monModele.index(index,1), Qt.DisplayRole)
                

                Now I'm adding the 2d data part.

                1 Reply Last reply
                0
                • GrecKoG Offline
                  GrecKoG Offline
                  GrecKo
                  Qt Champions 2018
                  wrote last edited by
                  #8

                  You should follow the following section to expose your properties as roles:
                  https://doc.qt.io/qt-6/qrangemodel.html#gadgets-and-objects-as-multi-role-items

                  You should then be able to write startY: model.y (with a required property QtObject model in the delegate)

                  GilboonetG 1 Reply Last reply
                  0
                  • GrecKoG GrecKo

                    You should follow the following section to expose your properties as roles:
                    https://doc.qt.io/qt-6/qrangemodel.html#gadgets-and-objects-as-multi-role-items

                    You should then be able to write startY: model.y (with a required property QtObject model in the delegate)

                    GilboonetG Offline
                    GilboonetG Offline
                    Gilboonet
                    wrote last edited by
                    #9

                    @GrecKo Thank you, I am using a Q_GADGET class but was missing the required property in the delegate for each property. Sadly adding the required property in the repeater is not enough. I tried to put it directly into the repeater after the model setting, there it is not initialized and get undefined as value.

                    My gadget class is as basic as possible, but I'm not able to set it up correctly to be able to use properties name on the QML side. For the moment I'm sticking to the mymodel.data(...index(...), Qt.DisplayRole) that works.

                    1 Reply Last reply
                    0
                    • SGaistS Offline
                      SGaistS Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote last edited by
                      #10

                      Can you share a minimal compilable application with your current code ?
                      That would allow us to check what is going on.

                      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
                      0
                      • GilboonetG Offline
                        GilboonetG Offline
                        Gilboonet
                        wrote last edited by
                        #11

                        the data class

                        #ifndef PIECE_H
                        #define PIECE_H
                        
                        
                        #include <QtCore/qobjectdefs.h>
                        #include <QtCore/qtmetamacros.h>
                        class piece
                        {
                            Q_GADGET
                            Q_PROPERTY(float x READ x WRITE setX)
                            Q_PROPERTY(float y READ y WRITE setY)
                        
                        public:
                            piece() : m_x(0.0f), m_y(0.0f) {}
                            piece(const piece &) = default;
                            piece(piece &&) = default;
                            piece &operator=(const piece &) = default;
                            piece &operator=(piece &&) = default;
                            explicit piece(const float &x, const float &y) : m_x(x), m_y(y) {}
                        
                            void setX(float newX) {
                                if (!qFuzzyCompare(newX, m_x))
                                    m_x = newX;
                            }
                            void setY(float newY) {
                                if (!qFuzzyCompare(newY, m_y))
                                    m_y = newY;
                            }
                        
                        private:
                            float m_x;
                            float m_y;
                            float x() const { return m_x; }
                            float y() const { return m_y; }
                        };
                        
                        #endif // PIECE_H
                        

                        main.cpp

                        #include "piece.h"
                        #include <QRangeModel>
                        #include <QGuiApplication>
                        #include <QQmlApplicationEngine>
                        
                        int main(int argc, char *argv[])
                        {
                            QGuiApplication app(argc, argv);
                        
                            QList<piece> mesPieces;
                            mesPieces.append(piece(20.0f, 20.0f));
                            mesPieces.append(piece(60.0f, 0.0f));
                            mesPieces.append(piece(100.0f, 30.0f));
                            QRangeModel modele(&mesPieces);
                        
                            QQmlApplicationEngine engine;
                            engine.setInitialProperties({{"monModele", QVariant::fromValue(&modele) }});
                        
                            QObject::connect(
                                &engine,
                                &QQmlApplicationEngine::objectCreationFailed,
                                &app,
                                []() { QCoreApplication::exit(-1); },
                                Qt::QueuedConnection);
                            engine.loadFromModule("testQRange1", "Main");
                        
                            return QGuiApplication::exec();
                        }
                        

                        main.qml

                        import QtQuick
                        import QtQuick.Controls.Basic
                        import QtQuick.Shapes
                        
                        ApplicationWindow {
                            id: window
                            width: 640
                            height: 480
                            visible: true
                            title: qsTr("test QRangeModel")
                        
                            required property AbstractItemModel monModele
                        
                            Repeater {
                                model : monModele
                                Shape {
                                    property real mx :   monModele.data(monModele.index(index, 0), Qt.DisplayRole)
                                    property real my :   monModele.data(monModele.index(index, 1), Qt.DisplayRole)
                                    anchors.fill: parent
                                    ShapePath {
                                        strokeColor: "yellow"
                                        fillColor: "yellow"
                                        strokeWidth: 1
                                        startX: mx
                                        startY: my
                                        PathLine { relativeX: 0; relativeY: 50 }
                                        PathLine { relativeX: 50; relativeY: 0 }
                                    }
                                }
                            }
                        }
                        

                        It doesn't work if I try to replace mx and my properties by required property real x (and y)

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