Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Best Practice to Add Dynamic QML from C++ data
Forum Updated to NodeBB v4.3 + New Features

Best Practice to Add Dynamic QML from C++ data

Scheduled Pinned Locked Moved Solved General and Desktop
27 Posts 4 Posters 12.5k Views 2 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.
  • p3c0P p3c0

    @Placeable

    So 'aDataStructOfData' would return a struct or even a class of Qt types, like maybe a QString, a QFont etc I could later use to populate in QML?

    Yes it is possible. Make sure your struct or class uses Q_OBJECT macro. This class can then contain Q_INVOKABLE functions or Q_PROPERTY's which will return your QString or QFont. These propeties or functions defined as such can then be access from QML.

    Since data require a QVariant you will require to return your class as a QVariant. For that you may require you class or struct to be registered using Q_DECLARE_METATYPE.

    case TextObj: 
         return QVariant::fromValue(someData.aDataStructOfData());
    
    VRoninV Offline
    VRoninV Offline
    VRonin
    wrote on last edited by
    #8

    @p3c0 Now I'm very interested here as on paper I see it happening but when I try and do it in practice I always fail.

    for example:
    TestData.h

    #ifndef TESTDATA_H
    #define TESTDATA_H
    #include <QString>
    #include <QMetaType>
    class TestData
    {
        Q_GADGET
        Q_PROPERTY(QString text READ text WRITE setText)
        Q_PROPERTY(QString desctiption READ desctiption WRITE setText)
    public:
        TestData(){}
        const QString& desctiption() const
        {
            return m_desctiption;
        }
        
        void setDesctiption(const QString &desctiption)
        {
            m_desctiption = desctiption;
        }
        const QString& text() const
        {
            return m_text;
        }
        
        void setText(const QString &text)
        {
            m_text = text;
        }
        
    private:
        QString m_text;
        QString m_desctiption;
    };
    Q_DECLARE_METATYPE(TestData)
    #endif // TESTDATA_H
    

    How would you use this as data in a model?

    What I always did is put text in a role, description in another and use them as QStrings

    "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
    ~Napoleon Bonaparte

    On a crusade to banish setIndexWidget() from the holy land of Qt

    kshegunovK 1 Reply Last reply
    0
    • VRoninV VRonin

      @p3c0 Now I'm very interested here as on paper I see it happening but when I try and do it in practice I always fail.

      for example:
      TestData.h

      #ifndef TESTDATA_H
      #define TESTDATA_H
      #include <QString>
      #include <QMetaType>
      class TestData
      {
          Q_GADGET
          Q_PROPERTY(QString text READ text WRITE setText)
          Q_PROPERTY(QString desctiption READ desctiption WRITE setText)
      public:
          TestData(){}
          const QString& desctiption() const
          {
              return m_desctiption;
          }
          
          void setDesctiption(const QString &desctiption)
          {
              m_desctiption = desctiption;
          }
          const QString& text() const
          {
              return m_text;
          }
          
          void setText(const QString &text)
          {
              m_text = text;
          }
          
      private:
          QString m_text;
          QString m_desctiption;
      };
      Q_DECLARE_METATYPE(TestData)
      #endif // TESTDATA_H
      

      How would you use this as data in a model?

      What I always did is put text in a role, description in another and use them as QStrings

      kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by
      #9

      With my rudimentary knowledge of QML I'd first register the type:

      qmlRegisterType<TestData>("com.testns", 1, 0, "TestData");
      

      And then you should be able to instantiate this in QML. However, I'm not convinced it'd work with gadgets, QML is pretty heavy on the QObject usage ... and honestly I don't know for sure this'd be enough for the type to be returned from a model.

      Read and abide by the Qt Code of Conduct

      VRoninV 1 Reply Last reply
      0
      • kshegunovK kshegunov

        With my rudimentary knowledge of QML I'd first register the type:

        qmlRegisterType<TestData>("com.testns", 1, 0, "TestData");
        

        And then you should be able to instantiate this in QML. However, I'm not convinced it'd work with gadgets, QML is pretty heavy on the QObject usage ... and honestly I don't know for sure this'd be enough for the type to be returned from a model.

        VRoninV Offline
        VRoninV Offline
        VRonin
        wrote on last edited by
        #10

        @kshegunov said in Best Practice to Add Dynamic QML from C++ data:

        And then you should be able to instantiate this in QML.

        Exactly, instantiate is easy but if you put that thing in a model in C++ and call it in QML how can you tell QML your QVariant is TestData?

        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
        ~Napoleon Bonaparte

        On a crusade to banish setIndexWidget() from the holy land of Qt

        kshegunovK 1 Reply Last reply
        0
        • VRoninV VRonin

          @kshegunov said in Best Practice to Add Dynamic QML from C++ data:

          And then you should be able to instantiate this in QML.

          Exactly, instantiate is easy but if you put that thing in a model in C++ and call it in QML how can you tell QML your QVariant is TestData?

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by
          #11

          @VRonin said in Best Practice to Add Dynamic QML from C++ data:

          how can you tell QML your QVariant is TestData?

          I don't follow. QML and Qt already know what is in the QVariant that's the purpose of declaring it as a meta type ... and qmlRegisterType should expose the interface of said class to the QML engine, if I'm correct.

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          0
          • VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by VRonin
            #12

            ok, let's say our main looks like this:

            #include <QApplication>
            #include <QQmlApplicationEngine>
            #include <QQmlContext>
            #include <QStandardItemModel>
            #include "testdata.h"
            int main(int argc, char *argv[])
            {
                QApplication app(argc, argv);
            
            
                QStandardItemModel testModel;
                testModel.insertRows(0,2);
                testModel.insertColumn(0);
            
                TestData tempTestData;
                tempTestData.setText("Data1Text");
                tempTestData.setDesctiption("Data1Desc");
                testModel.setData(testModel.index(0,0),QVariant::fromValue(tempTestData));
            
                tempTestData.setText("Data2Text");
                tempTestData.setDesctiption("Data2Desc");
                testModel.setData(testModel.index(1,0),QVariant::fromValue(tempTestData));
            
                QQmlApplicationEngine engine;
                qmlRegisterType<TestData>("com.testns", 1, 0, "TestData");
                engine.rootContext()->setContextProperty("testModel", &testModel);
                engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
                return app.exec();
            }
            

            and main.qml is

            import com.testns 1.0
            ApplicationWindow {
                visible: true
                width: 640
                height: 480
                title: "Testing Model"
            
                ListView{
                    model: testModel
                    delegate: ???
                }
            }
            
            

            What do you put in ??? to simply make text and description appear next to each others?

            The official example http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html splits the internals on Animal into two different roles each containing a QString, I never saw an example of a delegate handling custom variant types

            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
            ~Napoleon Bonaparte

            On a crusade to banish setIndexWidget() from the holy land of Qt

            kshegunovK 1 Reply Last reply
            0
            • VRoninV VRonin

              ok, let's say our main looks like this:

              #include <QApplication>
              #include <QQmlApplicationEngine>
              #include <QQmlContext>
              #include <QStandardItemModel>
              #include "testdata.h"
              int main(int argc, char *argv[])
              {
                  QApplication app(argc, argv);
              
              
                  QStandardItemModel testModel;
                  testModel.insertRows(0,2);
                  testModel.insertColumn(0);
              
                  TestData tempTestData;
                  tempTestData.setText("Data1Text");
                  tempTestData.setDesctiption("Data1Desc");
                  testModel.setData(testModel.index(0,0),QVariant::fromValue(tempTestData));
              
                  tempTestData.setText("Data2Text");
                  tempTestData.setDesctiption("Data2Desc");
                  testModel.setData(testModel.index(1,0),QVariant::fromValue(tempTestData));
              
                  QQmlApplicationEngine engine;
                  qmlRegisterType<TestData>("com.testns", 1, 0, "TestData");
                  engine.rootContext()->setContextProperty("testModel", &testModel);
                  engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
                  return app.exec();
              }
              

              and main.qml is

              import com.testns 1.0
              ApplicationWindow {
                  visible: true
                  width: 640
                  height: 480
                  title: "Testing Model"
              
                  ListView{
                      model: testModel
                      delegate: ???
                  }
              }
              
              

              What do you put in ??? to simply make text and description appear next to each others?

              The official example http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html splits the internals on Animal into two different roles each containing a QString, I never saw an example of a delegate handling custom variant types

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by kshegunov
              #13

              @VRonin

              Haven't tried it, but I'd do something along the lines of:

              ApplicationWindow  {
                  visible: true
                  width: 640
                  height: 480
                  title: "Testing Model"
              
                  ListView  {
                      model: testModel
                      delegate: Rectangle  {
                          height: 25
                          width: 100
                          Text  {
                              id: tmText
                              text: modelData.text
                              anchors.left: parent.left
                              anchors.top: parent.top
                              anchors.bottom: parent.bottom
                          }
                          Text  {
                              text: modelData.desctiption
                              anchors.left: tmText.right
                              anchors.right: parent.right
                              anchors.top: parent.top
                              anchors.bottom: parent.bottom
                          }
                      }
                  }
              }
              

              Read and abide by the Qt Code of Conduct

              VRoninV 1 Reply Last reply
              1
              • VRoninV Offline
                VRoninV Offline
                VRonin
                wrote on last edited by
                #14

                needs edit instead of modelData and it works! thanks so much

                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                ~Napoleon Bonaparte

                On a crusade to banish setIndexWidget() from the holy land of Qt

                kshegunovK 1 Reply Last reply
                1
                • VRoninV VRonin

                  needs edit instead of modelData and it works! thanks so much

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by kshegunov
                  #15

                  @VRonin said in Best Practice to Add Dynamic QML from C++ data:

                  needs edit instead of modelData and it works! thanks so much

                  I'm glad it does. :D
                  I was just putting down the code to test it ... thanks for sparing me the trouble :P

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  0
                  • P Offline
                    P Offline
                    Placeable
                    wrote on last edited by
                    #16

                    Okay I got it to work, here's what i got now, and it is a long post so bare with me, - I defined a class in the following way and mind you the QObject *parent in the constructor I always define as 0, I never send in anything there. Dunno if it is intended to do so.

                    class ComplexData : public QObject {
                        Q_OBJECT
                    
                        Q_PROPERTY(QString text READ testString WRITE setTestString NOTIFY testStringChanged)
                    
                    public:
                        explicit ComplexData(QObject *parent = 0);
                    
                        QString testString() const;
                        void setTestString(const QString &testString);
                    
                    signals:
                        void testStringChanged(QString);
                    
                    private:
                        QString mTestString;
                    };
                    

                    Implementation of ComplexData.cpp

                    ComplexData::ComplexData(QObject *parent) : QObject(parent) {
                        mTestString = "This is a test string from CPP!";
                    }
                    
                    QString ComplexData::testString() const {
                        return mTestString;
                    }
                    
                    void ComplexData::setTestString(const QString &testString) {
                        mTestString = testString;
                        emit testStringChanged(testString);
                    }
                    

                    My "SomeData" gets this new "ComplexData" as a member variable:

                    class SomeData
                    {
                    public:
                        SomeData(const QString &headerText);
                    
                        QString headerText() const;
                        ComplexData* complexData() const;
                    
                    private:
                        QString mHeaderText;
                        ComplexData* mComplexData; //::  Has to be a pointer for some reason
                    };
                    

                    Implementation of SomeData.cpp:

                    SomeData::SomeData(const QString &headerText)
                        : mHeaderText(headerText)
                    {
                        mComplexData = new ComplexData(); //:: I just create the object here for Testing could be passed into Constructor though
                    }
                    
                    ComplexData* SomeData::complexData() const {
                        return mComplexData;
                    }
                    

                    And finally in my AbstractListModel 'SomeListModel.cpp' data function now looks like this:

                    QVariant SomeListModel::data(const QModelIndex &index, int role) const {
                        if (index.row() < 0 || index.row() >= mSolutions.count())
                            return QVariant();
                    
                        const SomeData &someData= mData[index.row()];
                    
                        switch (role) {
                        case HeaderText:
                            return someData.headerText();
                        break;
                    
                        case ComplexData:
                            return QVariant::fromValue( someData.complexData() );
                        break;
                        }
                    
                        return QVariant();
                    }
                    

                    With this I can use this in QML like:

                    Text {
                          text: model.complexData.text
                    }
                    

                    Okay, all fine and dandy. I basically just moved some data to another class. I could've just kept on having this in the SomeData class. What I really need to know is if it is possible to return the whole "ComplexData" as a QML of type "Text", is that a QTextField type in CPP?

                    So instead in the data function of SomeListModel I would return say:

                    case ComplexData:
                            return QVariant::fromValue( someData.complexData() ); //::  This returns a formatted textfield setup from C++ to be used in QML instead.
                    break;
                    

                    Not even sure if that is possible though. I am getting a bit confused here :/

                    1 Reply Last reply
                    0
                    • p3c0P Offline
                      p3c0P Offline
                      p3c0
                      Moderators
                      wrote on last edited by
                      #17

                      @Placeable What do you mean by "formatted textfield setup" ?

                      157

                      P 1 Reply Last reply
                      0
                      • p3c0P p3c0

                        @Placeable What do you mean by "formatted textfield setup" ?

                        P Offline
                        P Offline
                        Placeable
                        wrote on last edited by Placeable
                        #18

                        @p3c0 said in Best Practice to Add Dynamic QML from C++ data:

                        @Placeable What do you mean by "formatted textfield setup" ?

                        In CPP I want to create a set of Texts to be used in QML - I read data from a JSON file and need to create Texts for QML dynamically. All data is stored on the CPP side so let's say I need to create a view with 3 Texts and another with 0 Texts. That is what I am struggling to do.

                        So I am wondering if it is possible to create these Texts in CPP (Whatever their CPP variant might be, I dunno) and then return this to be populated in said QML view by changing the QAbstractListModel class somehow.

                        1 Reply Last reply
                        0
                        • p3c0P Offline
                          p3c0P Offline
                          p3c0
                          Moderators
                          wrote on last edited by p3c0
                          #19

                          @Placeable Do you mean something like dynamic object creation in QML ?
                          And in your case you want the QML component's code(Text) will come from CPP ?

                          http://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html#creating-an-object-from-a-string-of-qml

                          157

                          P 1 Reply Last reply
                          0
                          • p3c0P p3c0

                            @Placeable Do you mean something like dynamic object creation in QML ?
                            And in your case you want the QML component's code(Text) will come from CPP ?

                            http://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html#creating-an-object-from-a-string-of-qml

                            P Offline
                            P Offline
                            Placeable
                            wrote on last edited by Placeable
                            #20

                            @p3c0
                            Exactly so, dynamically. Basically I want to setup these Texts on the CPP side (Font size, formatting as such etc) and let QML now: Hey here is a Text for you to use, have fun! Oh by the way here's another Text to use for this ListItem. But that ListItem over there you will get no Text to use at all! Hah!

                            So a ListItem could have a variable amount of Texts that I also need to somehow tell my View Delegate.

                            1 Reply Last reply
                            0
                            • p3c0P Offline
                              p3c0P Offline
                              p3c0
                              Moderators
                              wrote on last edited by
                              #21

                              @Placeable I have done something similar my project here. So what that particular code does is it creates a QQuickItem from a base QML template as shown here. Then sets some color and font on it. So you can try to do something similar. But remember QQmlComponent requires QQmlEngine. This is the same with which you must have loaded the QML initially. Also remember that dynamic QQuickItem also requires a visual parent which is set using setParent.

                              157

                              P 1 Reply Last reply
                              0
                              • p3c0P p3c0

                                @Placeable I have done something similar my project here. So what that particular code does is it creates a QQuickItem from a base QML template as shown here. Then sets some color and font on it. So you can try to do something similar. But remember QQmlComponent requires QQmlEngine. This is the same with which you must have loaded the QML initially. Also remember that dynamic QQuickItem also requires a visual parent which is set using setParent.

                                P Offline
                                P Offline
                                Placeable
                                wrote on last edited by Placeable
                                #22

                                @p3c0 This is great, I like how to create QML from CPP. I came up with a different solution though which I think works but not sure if it is "best practice" - I'd like to experiment the way you did it as well.

                                Here's how I have done it now to create "dynamic" Texts in QML from CPP data:

                                In my QAbstractListModel data function I have this field that returns a QVariant:

                                case TextData:
                                        return QVariant::fromValue( someData.textList() );
                                break;
                                

                                The implementation of that returns a QList of QObject:

                                QList<QObject*> SomeData::textList() const {
                                    return mTextList;
                                }
                                

                                I can populate this QList with my TextData class that derives QObject

                                TextData.cpp:

                                class TextData : public QObject {
                                    Q_OBJECT
                                
                                    Q_PROPERTY(QString text READ testString WRITE setTestString NOTIFY testStringChanged)
                                
                                public:
                                    explicit TextData(QObject *parent = 0);
                                
                                    QString testString() const;
                                    void setTestString(const QString &testString);
                                
                                signals:
                                    void testStringChanged(QString);
                                
                                private:
                                    QString mTestString;
                                };
                                

                                Then in QML I can do something like this now to create QML Texts and populate them from the CPP data:

                                Component.onCompleted: {
                                    var arr = model.textData;
                                    var component = Qt.createComponent("SomeTextLayout.qml");
                                    for ( var i = 0; i < arr.length; i++ ) {
                                       var txtObject = arr[i];
                                       var txtQml = component.createObject(someParentId);
                                       txtQml.text = txtObject.text;
                                       ... //Etc fill in more props from the txtObject
                                    }
                                }
                                

                                Again how this is performance wise I am not sure I am inclined to do the QML markup on the CPP side as well.
                                I'll mark this topic as Solved as I think there is a lot of great input here that would help anyone else in the future.

                                Thanks everyone!

                                1 Reply Last reply
                                0
                                • p3c0P Offline
                                  p3c0P Offline
                                  p3c0
                                  Moderators
                                  wrote on last edited by p3c0
                                  #23

                                  @Placeable Beware of Component.onCompleted.

                                  The order of running the onCompleted handlers is undefined.

                                  Due to this it could be possible that your initialized components may be not be available when required. May be create them when you require them.

                                  157

                                  P 1 Reply Last reply
                                  0
                                  • p3c0P p3c0

                                    @Placeable Beware of Component.onCompleted.

                                    The order of running the onCompleted handlers is undefined.

                                    Due to this it could be possible that your initialized components may be not be available when required. May be create them when you require them.

                                    P Offline
                                    P Offline
                                    Placeable
                                    wrote on last edited by
                                    #24

                                    @p3c0

                                    I am not sure what this means. From my testing I see no issues. You mean there is a chance onCompleted is finished before the nested component.createObject(..) is created for this.

                                    How would one solve this preferably?

                                    1 Reply Last reply
                                    0
                                    • p3c0P Offline
                                      p3c0P Offline
                                      p3c0
                                      Moderators
                                      wrote on last edited by
                                      #25

                                      @Placeable

                                      If you find no issues then continue. Just a warning if you find some odd behavior during your implementaion. In some of my cases I found that onCompleted is not the most reliable place for initialization of components. Sometimes it triggered earlier causing problems to the objects which were intialized in it and which were dependent on others.

                                      157

                                      P 1 Reply Last reply
                                      0
                                      • p3c0P p3c0

                                        @Placeable

                                        If you find no issues then continue. Just a warning if you find some odd behavior during your implementaion. In some of my cases I found that onCompleted is not the most reliable place for initialization of components. Sometimes it triggered earlier causing problems to the objects which were intialized in it and which were dependent on others.

                                        P Offline
                                        P Offline
                                        Placeable
                                        wrote on last edited by
                                        #26

                                        @p3c0 Alrighty, I'll bare that in mind. Thanks again!

                                        1 Reply Last reply
                                        0
                                        • kshegunovK kshegunov

                                          @VRonin

                                          Haven't tried it, but I'd do something along the lines of:

                                          ApplicationWindow  {
                                              visible: true
                                              width: 640
                                              height: 480
                                              title: "Testing Model"
                                          
                                              ListView  {
                                                  model: testModel
                                                  delegate: Rectangle  {
                                                      height: 25
                                                      width: 100
                                                      Text  {
                                                          id: tmText
                                                          text: modelData.text
                                                          anchors.left: parent.left
                                                          anchors.top: parent.top
                                                          anchors.bottom: parent.bottom
                                                      }
                                                      Text  {
                                                          text: modelData.desctiption
                                                          anchors.left: tmText.right
                                                          anchors.right: parent.right
                                                          anchors.top: parent.top
                                                          anchors.bottom: parent.bottom
                                                      }
                                                  }
                                              }
                                          }
                                          
                                          VRoninV Offline
                                          VRoninV Offline
                                          VRonin
                                          wrote on last edited by
                                          #27

                                          Since this was something new from me and the issue popped up again after a while
                                          I prepared a small wiki article that summarises the technique

                                          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                          ~Napoleon Bonaparte

                                          On a crusade to banish setIndexWidget() from the holy land of Qt

                                          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