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
QtWS25 Last Chance

Best Practice to Add Dynamic QML from C++ data

Scheduled Pinned Locked Moved Solved General and Desktop
27 Posts 4 Posters 11.9k 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.
  • P Offline
    P Offline
    Placeable
    wrote on 20 Sept 2016, 09:41 last edited by
    #1

    I am working with a ListView using a QAbstractListModel from C++ to adjust some of the QML data. However for each List Item delegate I need to populate some formatted text I parse from a JSON file on the C++ end, such as a header and body text and in some cases no text at all is used. So in other words a List Item delegate can have variable amount of QML Texts, formatted in different ways, or no Text at all.

    I am thinking of dynamically adding these text fields from C++ to a List Item but I'm not sure if that is the way to go. Perhaps the List Item delegate from QML can look at the model (Which is tied to C++) to see how many Text objects it need and create it from the QML side, if possible.

    I really don't know what is the best practice for this so any input would be appreciated.

    1 Reply Last reply
    0
    • V Offline
      V Offline
      VRonin
      wrote on 20 Sept 2016, 10:51 last edited by
      #2

      I can't say if it's a best practice but what I would do is save each text part in a different role of the model (you can use Qt::UserRole onward) and let the delegate read and render each of those roles

      "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
      • P Offline
        P Offline
        Placeable
        wrote on 20 Sept 2016, 14:49 last edited by
        #3

        I've done a few roles that are only simple variables at the moment in my QAbstractListModel. Can I make roles that is a C++ object in itself that returns QML structure for these Texts with its own formatting?

        Where can I read up more on Roles?

        1 Reply Last reply
        0
        • V Offline
          V Offline
          VRonin
          wrote on 20 Sept 2016, 15:27 last edited by VRonin
          #4

          QML is not great at handling custom C++ objects that are not QObjects (see http://doc.qt.io/qt-5/qtqml-cppintegration-data.html). The easiest route is to save each basic element in its own role.

          struct{
          QString m_text;
          QFont m_textFont;
          }
          

          can be replicated saving text in Qt::UserRole, and textFont in Qt::UserRole+1

          Where can I read up more on Roles?

          http://doc.qt.io/qt-5/model-view-programming.html#basic-concepts scroll to "Item roles" paragraph and http://doc.qt.io/qt-5/qtquick-modelviewsdata-modelview.html#models

          "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
          1
          • P Offline
            P Offline
            Placeable
            wrote on 21 Sept 2016, 09:39 last edited by Placeable
            #5

            This is great, thanks!

            But I have problem finding documentation on more complex User Roles. Could I implement a QAbstractListModel's data function to return a struct of data rather than a simple property for QML?

            Example:

            QVariant SomeListModel::data(const QModelIndex &index, int role) const {
                if (index.row() < 0 || index.row() >= mData.count())
                    return QVariant();
            
                const SomeData &someData = mData[index.row()];
            
                switch (role) {
                case HeaderText:  //::  Simple Property Role returns a QString
                    return someData.headerText();
                break;
            
                case TextObj:   //::  Return a more complex data structure, like a struct or class
                    return someData.aDataStructOfData();
                break;
            
                }
            
                return QVariant();
            }
            

            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?

            P 1 Reply Last reply 21 Sept 2016, 11:13
            0
            • V Offline
              V Offline
              VRonin
              wrote on 21 Sept 2016, 10:37 last edited by
              #6

              take a look at http://qmlbook.github.io/en/ch15/index.html#models-in-c

              Personally I had little luck integrating custom structs/classes as values in a model but I'm pretty sure it's just because I'm not that good rather than it being impossible

              "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
              • P Placeable
                21 Sept 2016, 09:39

                This is great, thanks!

                But I have problem finding documentation on more complex User Roles. Could I implement a QAbstractListModel's data function to return a struct of data rather than a simple property for QML?

                Example:

                QVariant SomeListModel::data(const QModelIndex &index, int role) const {
                    if (index.row() < 0 || index.row() >= mData.count())
                        return QVariant();
                
                    const SomeData &someData = mData[index.row()];
                
                    switch (role) {
                    case HeaderText:  //::  Simple Property Role returns a QString
                        return someData.headerText();
                    break;
                
                    case TextObj:   //::  Return a more complex data structure, like a struct or class
                        return someData.aDataStructOfData();
                    break;
                
                    }
                
                    return QVariant();
                }
                

                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?

                P Offline
                P Offline
                p3c0
                Moderators
                wrote on 21 Sept 2016, 11:13 last edited by
                #7

                @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());
                

                157

                V 1 Reply Last reply 21 Sept 2016, 12:04
                0
                • P p3c0
                  21 Sept 2016, 11:13

                  @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());
                  
                  V Offline
                  V Offline
                  VRonin
                  wrote on 21 Sept 2016, 12:04 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

                  K 1 Reply Last reply 21 Sept 2016, 12:50
                  0
                  • V VRonin
                    21 Sept 2016, 12:04

                    @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

                    K Away
                    K Away
                    kshegunov
                    Moderators
                    wrote on 21 Sept 2016, 12:50 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

                    V 1 Reply Last reply 21 Sept 2016, 13:38
                    0
                    • K kshegunov
                      21 Sept 2016, 12:50

                      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.

                      V Offline
                      V Offline
                      VRonin
                      wrote on 21 Sept 2016, 13:38 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

                      K 1 Reply Last reply 21 Sept 2016, 13:53
                      0
                      • V VRonin
                        21 Sept 2016, 13:38

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

                        K Away
                        K Away
                        kshegunov
                        Moderators
                        wrote on 21 Sept 2016, 13:53 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
                        • V Offline
                          V Offline
                          VRonin
                          wrote on 21 Sept 2016, 14:19 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

                          K 1 Reply Last reply 21 Sept 2016, 15:07
                          0
                          • V VRonin
                            21 Sept 2016, 14:19

                            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

                            K Away
                            K Away
                            kshegunov
                            Moderators
                            wrote on 21 Sept 2016, 15:07 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

                            V 1 Reply Last reply 10 Jan 2018, 19:27
                            1
                            • V Offline
                              V Offline
                              VRonin
                              wrote on 21 Sept 2016, 15:25 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

                              K 1 Reply Last reply 21 Sept 2016, 15:26
                              1
                              • V VRonin
                                21 Sept 2016, 15:25

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

                                K Away
                                K Away
                                kshegunov
                                Moderators
                                wrote on 21 Sept 2016, 15:26 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 22 Sept 2016, 06:41 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
                                  • P Offline
                                    P Offline
                                    p3c0
                                    Moderators
                                    wrote on 22 Sept 2016, 06:52 last edited by
                                    #17

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

                                    157

                                    P 1 Reply Last reply 22 Sept 2016, 06:58
                                    0
                                    • P p3c0
                                      22 Sept 2016, 06:52

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

                                      P Offline
                                      P Offline
                                      Placeable
                                      wrote on 22 Sept 2016, 06:58 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
                                      • P Offline
                                        P Offline
                                        p3c0
                                        Moderators
                                        wrote on 22 Sept 2016, 07:04 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 22 Sept 2016, 07:06
                                        0
                                        • P p3c0
                                          22 Sept 2016, 07:04

                                          @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 22 Sept 2016, 07:06 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

                                          6/27

                                          21 Sept 2016, 10:37

                                          topic:navigator.unread, 21
                                          • Login

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