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 Updated to NodeBB v4.3 + New Features

Access dynamic created ListModel elements from C++!

Scheduled Pinned Locked Moved QML and Qt Quick
12 Posts 6 Posters 12.4k 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.
  • R Offline
    R Offline
    Roland_R
    wrote on 15 Mar 2013, 20:52 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 16 Mar 2013, 07:12 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 18 Mar 2013, 11:52 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 18 Mar 2013, 15:51 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 18 Mar 2013, 18:37 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 20 Mar 2013, 13:16 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 21 Mar 2013, 07:56 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 25 Mar 2013, 07:56 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 4 Mar 2014, 08:51 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 4 Mar 2014, 09:29 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 4 Mar 2014, 13:51 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