Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Access dynamic created ListModel elements from C++!
Forum Update on Monday, May 27th 2025

Access dynamic created ListModel elements from C++!

Scheduled Pinned Locked Moved QML and Qt Quick
12 Posts 6 Posters 12.3k 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.
  • J Offline
    J Offline
    jackmack
    wrote on last edited by
    #1

    Hi all,

    how I can access from within C++ the elements of a ListModel?

    I write a application with a ListView. The content is set by an ListModel which is set dynamically during runtime. This is necessary because the number of elements is controlled by an config file loaded by the application.

    Any examples I found (Qt examples, google search, forums) uses always Models with fixed number of elements. That's bad!

    At first I used the VisualItemModel like
    @
    ...
    VisualItemModel {
    id: myModel
    objectName: "myModel"

        Rect1 {
        ...
        }
    
        Rect2 {
        ...
        }
    

    @
    But the model doesn't have any append() functions.

    The I changed to use a ListModel like that:
    @
    ...
    ListModel {
    id: myModel
    objectName: "myModel"

        ListElement {
            source: "Rect1.qml"
        }
        ListElement {
            source: "Rect2.qml"
        }
    

    ...
    @
    with a delegate like that:
    @
    Component {
    id: myDelegate
    Loader {
    source: model.source
    }
    }
    @
    That works fine.

    But the big question is how I can reference/get access to the list elements from C++?
    Items are only available trough its "objectName". How I can set the "objectName" of the elements?

    From C++ i tried this:
    @
    ...
    QObject* object = m_view->rootObject();
    QObject model = object->findChild<QObject>("myModel");
    if (model)
    ... // this is working!!
    ...
    QList<QObject*> list = model->findChildren<QObject*>("rect1"); // "rect1" is the 'objectName' in Rect1.qml
    if (list) // fails!!! No items where found with that name!!!
    ...
    @
    Have anybody an idea how I can get access to such dynamic created elements?

    Thanks a lot...
    jackmack

    [Edit: Please wrap code in @ tags in the future. Thanks! -- mlong]

    1 Reply Last reply
    0
    • R Offline
      R Offline
      Roland_R
      wrote on last edited by
      #2

      Hi,

      there is no public API to conveniently access QML elements from C++. Even when using private impl. classes (a no go) it looks quite dirty as you can see in "tst_qquicklistmodel.cpp":http://qt.gitorious.org/qt/qtdeclarative/blobs/stable/tests/auto/qml/qquicklistmodel/tst_qquicklistmodel.cpp

      You could write simple QML functions that provide you access to the model elements and then call QMetaObject::invokeMethod from C++ as explained in "Interacting with QML Objects from C++":http://doc-snapshot.qt-project.org/qt5-stable/qtqml/qtqml-cppintegration-interactqmlfromcpp.html#invoking-qml-methods. This may be a practical way for smaller projects/tasks.

      But it seems to be much cleaner, you manage your model data completely in C++ and implement a reasonable QML/JS callable interface. Details are provided in "Integrating QML and C++":http://doc-snapshot.qt-project.org/qt5-stable/qtqml/qtqml-cppintegration-topic.html.

      Beware:
      I am not a QML expert. I just had a closer look at C++ interfacing too.

      1 Reply Last reply
      0
      • U Offline
        U Offline
        utcenter
        wrote on last edited by
        #3

        It is a counter-productive thing to access QML from C++. It defeats the whole purpose of UI and logic abstraction. The idea is to be able to access C++ functionality from C++ but not the other way around. Even though it is possible to like... get a pointer to a specific QML element you should not get in the practice of reaching from C++ into the QML world.

        1 Reply Last reply
        0
        • J Offline
          J Offline
          jackmack
          wrote on last edited by
          #4

          [quote author="utcenter" date="1363417941"]It is a counter-productive thing to access QML from C++. It defeats the whole purpose of UI and logic abstraction. The idea is to be able to access C++ functionality from C++ but not the other way around. Even though it is possible to like... get a pointer to a specific QML element you should not get in the practice of reaching from C++ into the QML world.[/quote]

          I know the purpose of UI and logic abstraction! And i try to do that!
          Based on that pupose I need a code example to imagine it by myself how I can do that. At the moment I don't know how I can solve my situation described above.

          Please give me an example how I can call a JS function of "Rect2.qml" from C++. That's typical the logic layer is changing somewhat and the UI has to show it.

          1 Reply Last reply
          0
          • A Offline
            A Offline
            andre
            wrote on last edited by
            #5

            You don't call from C++ into QML or JS. What you do is that you expose an object with a signal on it, and you emit that signal. Whatever happens in response to that, is out of the concern of your C++ code.

            1 Reply Last reply
            0
            • R Offline
              R Offline
              Roland_R
              wrote on last edited by
              #6

              Adding a getChild function to your model first

              @
              ListModel {
              id: myModel
              objectName: "myModel"

                  ListElement {
                      source: "Rect1.qml"
                  }
                  ListElement {
                      source: "Rect2.qml"
                  }
                  function getChild(index)
                  {
                      return get(index)
                  }
              

              @

              Then from C++ you can access a model item this way

              @
              // get number of items
              QVariant count = QQmlProperty::read( model, "count" );
              if (count.isValid())
              qDebug() << "count: " << count;

              // get element #0
              QVariant index = 0;
              bool succeeded = QMetaObject::invokeMethod(
                  model, "getChild", Q_RETURN_ARG(QVariant, retValue), Q_ARG( QVariant, index ) );
              if (succeeded)
              {
                  const QObject *child = qvariant_cast<QObject *>( retValue );
              
                  QVariant name = QQmlProperty::read( child, "name" );
                  if (name.isValid())
                      qDebug() << "child.name: " << name;
              }
              

              @

              Ugly as hell but it should work.

              1 Reply Last reply
              0
              • J Offline
                J Offline
                jackmack
                wrote on last edited by
                #7

                ->Roland_R
                The "ugly as hell" works. But there is an other issue! Communicate from QML to C++ doesn't work. I used an C++ exposed class with signals/slots.

                I think the used Loader component in the delegate creates the *.qml components/objects with their own context - not in the main.qml context. Therefore I don't have access to my exposed class. Strange: Nothing is written to the console like "unknown to find my exposed class". Only no calls of event handlers or calling methods works :-(

                Using of example:
                @(.h)
                ...
                signals:
                void enableComponent();
                ...

                (.cpp)
                ...
                emit enableComponent();
                ...

                (.qml)
                ...
                Connections {
                target: MyBinding
                onEnableComponent: {
                console.log("rect1")
                }
                }
                ...@

                To avoid this and have fully access uand functionality of exposes classes I have to use VisualItemModel instead of ListModel.

                ->Andre:
                Yes, I think this is the correct way. Using exposed classes can be uses for vice-versa communication QML/C++.

                But in my case there is an evil restriction of using VisualItemModel: No append(), remove() methods!

                For reminder:
                My application should be able to set the number of pages of a ListView depending of a xml config file which is loaded at runtime.

                VisualItemModel works only with static items like

                @main.qml

                VisualItemModel {
                    id: myModel
                    objectName: "myModel"
                
                    Rect1 {}
                }
                

                @

                But know I found a hack/workaround:
                "Changing the model of the VisualItemModel" ;-)

                Calling a JS function at runtime from C++ like that:

                @ function changeModel()
                {
                var str;
                str = "import QtQuick 1.1\n
                Item {\n
                id: myComp\n
                property alias count: myModel.count\n
                property alias model: myModel\n
                \n
                VisualItemModel {\n
                id: myModel\n
                objectName: "myModel"\n
                Rect2 {}\n
                Rect3 {}\n
                }\n
                }"

                    var o = Qt.createQmlObject(str, mainRect)
                
                    view.model = o.model;
                }
                

                @

                That works!

                At runtime I can prepare a string containing the qml code and pass it to the changeModel() as parameter.

                Strange, but works... (await I will find next restrictions).

                1 Reply Last reply
                0
                • T Offline
                  T Offline
                  Torgeir
                  wrote on last edited by
                  #8

                  This works, but seems unnatural (or hacky/ugly :-) ) for me that the C++ code "reaches" into the QML world and depends on particular functions or named objects to be present.

                  I don't know the full scope of requirements for your project, but would it be possible to implement the model in C++? Derive from QAbstractListModel:

                  @
                  class MyCppModel : public QAbstractListModel
                  {
                  Q_OBJECT
                  public:
                  explicit MyCppModel(QObject *parent = 0) : QAbstractListModel(parent) {}
                  int rowCount(const QModelIndex & parent = QModelIndex()) const {
                  return itemSources.count();
                  }

                  QVariant data(const QModelIndex &index, int role) const {
                      if (role == ItemSourceRole)
                         return itemSources.at(index.row());
                     else
                         return QVariant();
                    }
                  

                  public slots:
                  void append(QString itemSource) {
                  beginInsertRows(itemSources.count(), itemSources.count());
                  itemSources.append(itemSource);
                  endInsertRows();
                  }

                  protected:
                  enum Roles { ItemSourceRole : Qt::UserRoles + 1 }
                  QHash<int, QByteArray> roleNames() const {
                  QHash<int, QByteArray> roles;
                  roles[ItemSourceRole] = "item_source";
                  return roles;
                  }
                  private:
                  QList<QString> itemSources;
                  @

                  in main.cpp:
                  @qmlRegisterType<MyCppModel>("my.components", 1, 0, "MyCppModel");@

                  in QML:
                  @
                  MyCppModel {
                  id: myModel
                  onComponentCompleted: {
                  myModel.append("Rect1.qml")
                  myModel.append("Rect2.qml")
                  }
                  }

                  Column {
                  Repeater {
                  model: myModel
                  Loader { source: item_source }
                  }
                  }
                  @

                  (Code not tested, just written to show the main idea)

                  Since the model is in C++, any other C++ code could also call append() to add items to the model.
                  This way you don't tie the C++ backend to any particular QML frontend.

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    jackmack
                    wrote on last edited by
                    #9

                    Thanks. When I have more time I will check your suggestion to implement a C++ model.

                    1 Reply Last reply
                    0
                    • X Offline
                      X Offline
                      xiangzhai
                      wrote on last edited by
                      #10

                      Hi Torgeir, you are so cool to qmlRegisterType a customized C++ Model for QML usage.

                      But it is much easier to @findChild@ by @objectName@ and @setProperty@ the @QQuickListView@ property model.

                      Here is my example,

                      in QML:
                      @
                      ...
                      ListView {
                      objectName: "categoryListView"
                      model: categoryModel
                      ...
                      @

                      in C++:
                      @
                      QQmlApplicationEngine engine(QUrl("qml/main.qml"));
                      QObject *topLevel = engine.rootObjects().value(0);

                      QObject *categoryListView = topLevel->findChild<QObject *>("categoryListView");
                      qDebug() << categoryListView;
                      QStringList dataList;
                      for (int i = 1; i < 19; i++)
                      dataList.append("Category " + QString::number(i));
                      categoryListView->setProperty("model", QVariant::fromValue(dataList));
                      @

                      snapshot shown as https://www.dropbox.com/s/t03szfgyidohsoy/抓图2.png

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        andre
                        wrote on last edited by
                        #11

                        [quote author="xiangzhai" date="1393923065"]Hi Torgeir, you are so cool to qmlRegisterType a customized C++ Model for QML usage.

                        But it is much easier to @findChild@ by @objectName@ and @setProperty@ the @QQuickListView@ property model.
                        [/quote]
                        It depends on the situation what is easier. I think Torgeir is right that it is a bit weird to have to "reach into" the QML tree from C++. It is actually discouraged by the Qt documentation to reach deeply into the tree if you can avoid it. I find it more elegant to not have to depend on manually setting an objectName in the QML: way less brittle.

                        I'd advise your approach for small toy projects and experiments, but Torgeirs approach for anything serious.

                        1 Reply Last reply
                        0
                        • X Offline
                          X Offline
                          xiangzhai
                          wrote on last edited by
                          #12

                          Hi Andre,

                          Yes, it depends :)

                          And I find another way to play with ListView model

                          1. qmlRegisterType a customized C++ Model such as:
                            @
                            qmlRegisterType<MyModel>("cn.com.isoft.demo", 1, 0, "MyModel");
                            @

                          2. QStringList property such as:
                            @
                            Q_PROPERTY(QStringList myLists READ myLists NOTIFY myListsChanged)
                            @

                          3. put MyModel in QML such as:
                            @
                            import cn.com.i-soft.demo 1.0
                            ...
                            ListView {
                            ...
                            MyModel {
                            id: myModel
                            }

                            model: myModel.myLists
                            delegate: Rectangle {
                            ...
                            Text { text: modelData }
                            }
                            }
                            @

                          Regards,
                          Leslie Zhai

                          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