qt笔记之qml和C++的交互系列(二):rootObject

一.使用rootObject例程

1.运行

在这里插入图片描述

2.main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>

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

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty()) {
        qDebug() << "No root objects found.";
        return -1;
    }

    QObject *rootObject = engine.rootObjects().first();
    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle", Qt::FindChildrenRecursively);

    if (rectangle) {
        // 修改属性
        rectangle->setProperty("width", 200);
        // 调用方法
        QVariant newColor = "blue";
        QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, newColor));
    } else {
        qDebug() << "Rectangle object not found.";
        return -1;
    }

    return app.exec();
}

3.main.qml
import QtQuick 2.0
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: "Hello World"

    Rectangle {
        id: myRectangle
        objectName: "myRectangle"  // 添加 objectName
        width: 100
        height: 100
        color: "red"

        function changeColor(newColor) {
            console.log("Changing color to: " + newColor)
            color = newColor;
        }
    }
}

3.用于Debug的加长版main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>

void printObjectTree(QObject *object, int level = 0) {
    if (!object) return;
    QString indent(level * 2, ' ');
    qDebug() << indent << object->objectName() << "(" << object->metaObject()->className() << ")";
    const QObjectList children = object->children();
    for (QObject *child : children) {
        printObjectTree(child, level + 1);
    }
}

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

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty()) {
        qDebug() << "No root objects found.";
        return -1;
    }

    QObject *rootObject = engine.rootObjects().first();
    if (!rootObject) {
        qDebug() << "Root object not found.";
        return -1;
    }

    qDebug() << "Root object found. Printing object tree:";
    printObjectTree(rootObject);

    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle", Qt::FindChildrenRecursively);
    if (!rectangle) {
        qDebug() << "Rectangle object not found.";
        return -1;
    } else {
        qDebug() << "Rectangle object found.";
    }

    // 修改属性
    if (rectangle->setProperty("width", 200)) {
        qDebug() << "Width changed successfully.";
    } else {
        qDebug() << "Failed to change width.";
    }

    // 调用方法
    QVariant newColor = "blue";
    if (QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, newColor))) {
        qDebug() << "changeColor method called successfully.";
    } else {
        qDebug() << "Failed to call changeColor method.";
    }

    return app.exec();
}

运行打印

Root object found. Printing object tree:
"" "" ( ApplicationWindow_QMLTYPE_0 )
"  " "" ( QQuickRootItem )
"    " "" ( QQuickOverlay )
"      " "" ( QQmlComponent )
"      " "" ( QQmlComponent )
"    " "ApplicationWindow" ( QQuickContentItem )
"  " "myRectangle" ( QQuickRectangle_QML_3 )
Rectangle object found.
Width changed successfully.
qml: Changing color to: blue
changeColor method called successfully.

二.QML文件的根对象详解

在 QML(Qt Meta Language)中,QML 文件的根对象是整个用户界面组件的基础。根对象定义了该文件中所有子对象的上下文和作用域。通常,根对象是一个 UI 组件,比如 RectangleItemApplicationWindow 等,以下是对 QML 文件根对象的详解:

基本概念
  1. 唯一性

    • 每个 QML 文件必须且只能有一个根对象。这是 QML 文件的入口点,所有其他元素都必须嵌套在这个根对象之内。
  2. 类型选择

    • 根对象可以是任何 QML 类型,但通常会选择合适的 UI 组件,根据需要创建不同类型的根对象。例如,可以使用 Rectangle 创建一个矩形区域,或者使用 ApplicationWindow 创建一个完整的应用窗口。
  3. 作用域

    • 根对象定义了一个新的作用域,文件内的所有子对象都在这个作用域内。这意味着根对象的属性和方法可以被子对象访问和修改。
常见根对象类型
Rectangle
Rectangle {
    width: 200
    height: 200
    color: "lightblue"

    Text {
        text: "Hello, QML!"
        anchors.centerIn: parent
    }
}
  • Rectangle 是一个常见的根对象类型,用于定义一个矩形区域。
ApplicationWindow
import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: "QML Application"

    Button {
        text: "Click Me"
        anchors.centerIn: parent
    }
}
  • ApplicationWindow 用于创建一个完整的应用窗口,通常用于更复杂的应用程序。
属性和方法

根对象可以包含属性和方法,这些属性和方法可以在子对象中访问。例如:

Rectangle {
    id: root
    width: 300
    height: 300
    color: "lightgreen"

    function changeColor(newColor) {
        root.color = newColor
    }

    MouseArea {
        anchors.fill: parent
        onClicked: root.changeColor("lightcoral")
    }
}
作用域和继承

根对象中的属性和方法可以被子对象继承和使用。例如:

Rectangle {
    id: root
    width: 400
    height: 400
    color: "lightyellow"

    property string message: "Hello from root"

    Text {
        text: root.message
        anchors.centerIn: parent
    }
}

三.使用rootObject通过QObjectsetProperty方法,可以设置QML对象的属性rootObjects和 QMetaObject::invokeMethod的结合使用调用QML中的方法

3.1.运行

在这里插入图片描述

3.2.main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickItem>
#include <QMetaObject>

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

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    QObject *rootObject = engine.rootObjects().first();
    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle");

    if (rectangle) {
        qDebug() << "Rectangle found!";
        rectangle->setProperty("width", 300);
        rectangle->setProperty("color", "red");
        QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, "green"));
    } else {
        qDebug() << "Rectangle not found!";
    }

    return app.exec();
}

3.3.main.qml
import QtQuick 2.0
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    Rectangle {
        id: myRectangle
        objectName: "myRectangle"  // 添加 objectName
        width: 200
        height: 200
        color: "blue"

        function changeColor(newColor) {
            color = newColor;
        }
    }
}

四.理解rootObjectsQMetaObject::invokeMethod的结合使用

rootObjects 和 QMetaObject::invokeMethod 可以结合使用,特别是在需要从 C++ 调用 QML 中的方法时。让我们详细解释一下这个过程,并澄清为什么 QMetaObject::invokeMethod 不能单独用于调用 QML 中的方法。

理解 rootObjects 和 QMetaObject::invokeMethod 的结合使用
  1. 获取 QML 根对象:

    • 使用 rootObjects 获取 QML 加载后的根对象。这一步是必要的,因为需要一个具体的 QML 对象实例来调用其方法。
  2. 使用 QMetaObject::invokeMethod 调用 QML 方法:

    • 一旦有了 QML 的根对象(或任何需要调用方法的对象),可以使用 QMetaObject::invokeMethod 动态调用其方法。
详细示例

假设有一个简单的 QML 文件 main.qml,其中定义了一个方法 myQmlMethod

import QtQuick 2.0

Rectangle {
    width: 200
    height: 200

    function myQmlMethod() {
        console.log("myQmlMethod called")
    }
}

在 C++ 中,可以按以下步骤调用这个方法:

1. 加载 QML 文件并获取根对象
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QMetaObject>

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

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    QObject *rootObject = engine.rootObjects().first();

    // Now use QMetaObject::invokeMethod to call the QML method
    QMetaObject::invokeMethod(rootObject, "myQmlMethod");

    return app.exec();
}

2. 动态调用 QML 方法
// Assuming rootObject is already obtained as shown above
QMetaObject::invokeMethod(rootObject, "myQmlMethod");
为什么 QMetaObject::invokeMethod 不能单独使用?

QMetaObject::invokeMethod 需要一个具体的对象实例以及方法名称来调用方法。它不直接与 QML 交互,而是用于调用任何 QObject 派生类的实例方法。因此,必须首先通过 rootObjects 获取 QML 加载后的对象实例,然后才能使用 QMetaObject::invokeMethod 调用该对象的方法。

总结
  • rootObjects: 用于获取 QML 加载后的根对象。
  • QMetaObject::invokeMethod: 用于动态调用 QObject 实例的方法。

这两者结合使用时,首先通过 rootObjects 获取 QML 对象实例,然后使用 QMetaObject::invokeMethod 调用该对象的方法。这样可以在 C++ 代码中灵活地调用 QML 中定义的方法。

五.rootObjects是一个QObject列表,包含了通过该引擎加载的QML文档的根对象

属性/方法描述
rootObjects返回一个包含所有通过该引擎加载的 QML 根对象的列表。
append()添加一个对象到 rootObjects 列表。
at(int index)返回 rootObjects 列表中指定索引处的对象。
clear()清空 rootObjects 列表。
contains(QObject* obj)检查 rootObjects 列表是否包含指定的对象。
indexOf(QObject* obj)返回指定对象在 rootObjects 列表中的索引,如果不包含返回 -1。
removeAt(int index)移除 rootObjects 列表中指定索引处的对象。
size()返回 rootObjects 列表的大小。
first()返回 rootObjects 列表中的第一个对象。
last()返回 rootObjects 列表中的最后一个对象。
作用
  1. 访问根对象: 可以方便地访问通过 QML 加载的主界面对象,通常用于设置属性或调用方法。
  2. 对象管理: 允许在运行时动态地管理 QML 根对象,例如添加、移除或替换根对象。
  3. 调试和测试: 在调试和测试过程中,可以通过 rootObjects 访问和操作 QML 对象树进行检查和验证。
示例代码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>

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

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    // 获取第一个根对象
    QObject *rootObject = engine.rootObjects().first();
    qDebug() << "Root object:" << rootObject;

    return app.exec();
}

在这个示例中,engine.rootObjects().first() 获取并打印了加载的 QML 文档的根对象。

六.为什么findChild<QObject*>(“myRectangle”) 不起作用:id vs objectName

在 Qt 中,findChild 方法用于查找对象树中的子对象。默认情况下,它只会查找直接的子对象,而不会递归地查找整个对象树。此外,findChild 方法通常依赖于对象的 objectName 属性,而不是 id 属性。

id vs. objectName
  • id:在 QML 中,id 是一个局部标识符,用于在 QML 文件中引用对象。它在 C++ 层不可见。
  • objectName:这是一个标准的 Qt 属性,可用于在 C++ 和 QML 层之间进行对象查找和引用。
为什么 findChild<QObject*>("myRectangle") 不起作用

提到的代码:

QObject *rectangle = rootObject->findChild<QObject*>("myRectangle");

如果没有设置 objectName,这个方法不会成功,因为它只查找对象的 objectName 属性,而不是 id

为什么 objectName 有效

当将 objectName 设置为 "myRectangle" 时,findChild 方法可以成功找到对象:

Rectangle {
    id: myRectangle
    objectName: "myRectangle"  // 添加 objectName
    ...
}

在 C++ 中:

QObject *rectangle = rootObject->findChild<QObject*>("myRectangle", Qt::FindChildrenRecursively);

使用 Qt::FindChildrenRecursively 标志可以确保在对象树中递归查找。

如何仅使用 findChild<QObject*>("myRectangle")

如果希望不使用递归查找,且对象位于 rootObject 的直接子对象中,需要确保对象的 objectName 已正确设置,并且对象确实是 rootObject 的直接子对象。

示例
QML 文件:main.qml
import QtQuick 2.0
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    Rectangle {
        id: myRectangle
        objectName: "myRectangle"  // 添加 objectName
        width: 200
        height: 200
        color: "blue"

        function changeColor(newColor) {
            color = newColor;
        }
    }
}

C++ 文件:main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickItem>
#include <QMetaObject>

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

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    QObject *rootObject = engine.rootObjects().first();
    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle");

    if (rectangle) {
        qDebug() << "Rectangle found!";
        rectangle->setProperty("width", 300);
        rectangle->setProperty("color", "red");
        QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, "green"));
    } else {
        qDebug() << "Rectangle not found!";
    }

    return app.exec();
}

总结

为了在 C++ 代码中成功找到 QML 对象,需要确保以下几点:

  1. 在 QML 中设置对象的 objectName 属性。
  2. 使用 findChild 方法进行对象查找时,查找的是 objectName,而不是 id
  3. 如果对象不是直接子对象,使用 Qt::FindChildrenRecursively 标志进行递归查找。

这样做可以确保能够在 C++ 代码中准确查找到所需的 QML 对象。

  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值