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.3k 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.
  • 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