How to access a nested QML object from C++?
Asked Answered
P

3

14

Here is a reproducible example:

main.qml


import QtQuick 2.0

Item {
    id : root
    width: 360
    height: 360

    Text {
        id : t1
        text: qsTr("Hello World")
        property int someNumber: 1000
        anchors.centerIn: parent
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }
}

main.cpp


#include <QtGui/QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQmlProperty>
#include <QDebug>

#include "qtquick2applicationviewer.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QtQuick2ApplicationViewer viewer;
    viewer.setMainQmlFile(QStringLiteral("qml/untitled/main.qml"));
    viewer.showExpanded();

    QQmlEngine engine;
    QQmlComponent component(&engine, "qml/untitled/main.qml");
    QObject *object = component.create();

    qDebug() << "Property value:" << QQmlProperty::read(object, "root.t1.someNumber").toInt();

    return app.exec();
}

I wish to access the property somenumber of the text of the QML Item. The above method isn't producing the desired result.

How to do it?

Preestablish answered 27/12, 2013 at 12:36 Comment(5)
Why not property alias?Dependent
@LaszloPapp didn't understand, please explain.Preestablish
property alias mytext: t1.text for the root item, or 'QObject *object = object->findChild<QObject*>("t1");`Dependent
In QML you title the Text "t1" but in C++ you access it as "ti". Typo?Gyre
@StefanMonov that may be typo. Thanks for pointing out.Preestablish
L
11

You have two ways (at least) to accomplish this depending on your personal preference.

QML code extension

You can add a property alias to the root item as follows:

import QtQuick 2.0

Item {
    id : root
    width: 360
    height: 360

    property alias mySomeNumber: t1.someNumber // This is the addition

    Text {
        id : t1
        text: qsTr("Hello World")
        property int someNumber: 1000
        anchors.centerIn: parent
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }
}

C++ code extension

Since the QML items are QObject, you can look for the children explicitly as well, just as you would do it in a C++ QObject hierarchy. The code would be something like this:

#include <QtGui/QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQmlProperty>
#include <QDebug>

#include "qtquick2applicationviewer.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QtQuick2ApplicationViewer viewer;
    viewer.setMainQmlFile(QStringLiteral("qml/untitled/main.qml"));
    viewer.showExpanded();

    QQmlEngine engine;
    QQmlComponent component(&engine, "qml/untitled/main.qml");
    QObject *object = component.create();

    // This line is added

    QObject *childObject = object->findChild<QObject*>("SomeNumberText");

    // The following line is modified respectively

    qDebug() << "Property value:" << QQmlProperty::read(childObject, "someNumber").toInt();

    return app.exec();
}

However, this means you will need to add the objectName: "SomeNumberText" line to your Text child item in the qml file.

Lollar answered 27/12, 2013 at 12:45 Comment(6)
findChild searches for an object with the given objectName property value, which is not equal to QML's id. If we assume that the QML code should not be modified, that's not a solution. Otherwise, we have to add objectName: "tr1" in QML.Emulsify
@leemes: you are right and too fast. I was busy with adding lotta more code and fixing bugs in the initial version. ;-)Dependent
Do you know any way to find a QML item by using its id? This would allow us to find items without adding an objectName.Tweeter
@pedromateo: as far as I know "id" is a qml property usually, so naturally, that would not work for all QObjects. Now, it might be possible that some QML C++ class will have some qml specific children finder algorithm, but I am not sure if it is common enough a use case.Dependent
@Ipapp: you are right. I read that id does exist only in QML layer, and not at QObject level.Tweeter
pedromateo: the QML id property is special, i.e. not like other properties. See qt-project.org/doc/qt-5/qtqml-syntax-propertybinding.html. You can't access its value (dereference, or read it, as in 'foo = id') like other properties, in QML or in C++.Rhapsodist
T
2

Here you can find a recursive method looking for a QML item by objectName and starting at QQmlApplicationEngine::rootObjects():

////
static QQuickItem* FindItemByName(QList<QObject*> nodes, const QString& name)
{
    for(int i = 0; i < nodes.size(); i++){
        // search for node
        if (nodes.at(i) && nodes.at(i)->objectName() == name){
            return dynamic_cast<QQuickItem*>(nodes.at(i));
        }
        // search in children
        else if (nodes.at(i) && nodes.at(i)->children().size() > 0){
            QQuickItem* item = FindItemByName(nodes.at(i)->children(), name);
            if (item)
                return item;
        }
    }
    // not found
    return NULL;
}

///
static QQuickItem* FindItemByName(QQmlApplicationEngine* engine, const QString& name)
{
    return FindItemByName(engine->rootObjects(), name);
}
Tweeter answered 24/10, 2014 at 8:39 Comment(2)
Why not just call QObject::findChild for each item in QQmlApplicationEngine::rootObjects()? It's recursive by default. Seems like you're reinventing the wheel.Gyre
No, it's not. In fact, the very reason of it not being recursive is what brought me here.Clean
R
0

What is the use case for this? It might be better to just treat the [text, someNumber] struct or object as the model. Then you only need to find the model object. Or you could create the model object on the C++ side and set it in the QML context. You could access the model and its nested properties in QML:

Text {
        text: model.text
        property int someNumber: model.someNumber
    }
Rhapsodist answered 30/11, 2014 at 15:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.