当前位置: 代码网 > it编程>编程语言>C/C++ > qt笔记之qml和C++的交互系列(二):rootObject

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

2024年08月01日 C/C++ 我要评论
用于获取 QML 加载后的根对象。: 用于动态调用QObject实例的方法。这两者结合使用时,首先通过获取 QML 对象实例,然后使用调用该对象的方法。这样可以在 C++ 代码中灵活地调用 QML 中定义的方法。在 QML 中设置对象的objectName属性。使用findChild方法进行对象查找时,查找的是objectName,而不是id。如果对象不是直接子对象,使用标志进行递归查找。这样做可以确保能够在 C++ 代码中准确查找到所需的 QML 对象。

一.使用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 对象。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com