qt笔记之qml和C++的交互系列(二):rootObject
code review!
—— 2024-06-17 杭州 夜
文章目录
- qt笔记之qml和C++的交互系列(二):rootObject
- 一.使用rootObject例程
- 1.运行
- 2.main.cpp
- 3.main.qml
- 3.用于Debug的加长版main.cpp
- 二.QML文件的根对象详解
- 基本概念
- 常见根对象类型
- Rectangle
- ApplicationWindow
- 属性和方法
- 作用域和继承
- 三.使用`rootObject`通过`QObject`的`setProperty`方法,可以设置`QML对象的属性`。`rootObjects`和 `QMetaObject::invokeMethod`的结合使用调用`QML中的方法`
- 3.1.运行
- 3.2.main.cpp
- 3.3.main.qml
- 四.理解`rootObjects`和`QMetaObject::invokeMethod`的结合使用
- 理解 `rootObjects` 和 `QMetaObject::invokeMethod` 的结合使用
- 详细示例
- 1. 加载 QML 文件并获取根对象
- 2. 动态调用 QML 方法
- 为什么 `QMetaObject::invokeMethod` 不能单独使用?
- 总结
- 五.`rootObjects`是一个`QObject列表`,包含了通过该引擎加载的QML文档的根对象
- 作用
- 示例代码
- 六.为什么findChild<QObject*>("myRectangle") 不起作用:`id` vs `objectName`
- `id` vs. `objectName`
- 为什么 `findChild<QObject*>("myRectangle")` 不起作用
- 为什么 `objectName` 有效
- 如何仅使用 `findChild<QObject*>("myRectangle")`
- 示例
- QML 文件:`main.qml`
- C++ 文件:`main.cpp`
- 总结
一.使用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 组件,比如 Rectangle
、Item
、ApplicationWindow
等,以下是对 QML 文件根对象的详解:
基本概念
-
唯一性:
- 每个 QML 文件必须且只能有一个根对象。这是 QML 文件的入口点,所有其他元素都必须嵌套在这个根对象之内。
-
类型选择:
- 根对象可以是任何 QML 类型,但通常会选择合适的 UI 组件,根据需要创建不同类型的根对象。例如,可以使用
Rectangle
创建一个矩形区域,或者使用ApplicationWindow
创建一个完整的应用窗口。
- 根对象可以是任何 QML 类型,但通常会选择合适的 UI 组件,根据需要创建不同类型的根对象。例如,可以使用
-
作用域:
- 根对象定义了一个新的作用域,文件内的所有子对象都在这个作用域内。这意味着根对象的属性和方法可以被子对象访问和修改。
常见根对象类型
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
通过QObject
的setProperty
方法,可以设置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;
}
}
}
四.理解rootObjects
和QMetaObject::invokeMethod
的结合使用
rootObjects
和 QMetaObject::invokeMethod
可以结合使用,特别是在需要从 C++ 调用 QML 中的方法时。让我们详细解释一下这个过程,并澄清为什么 QMetaObject::invokeMethod
不能单独用于调用 QML 中的方法。
理解 rootObjects
和 QMetaObject::invokeMethod
的结合使用
-
获取 QML 根对象:
- 使用
rootObjects
获取 QML 加载后的根对象。这一步是必要的,因为需要一个具体的 QML 对象实例来调用其方法。
- 使用
-
使用
QMetaObject::invokeMethod
调用 QML 方法:- 一旦有了 QML 的根对象(或任何需要调用方法的对象),可以使用
QMetaObject::invokeMethod
动态调用其方法。
- 一旦有了 QML 的根对象(或任何需要调用方法的对象),可以使用
详细示例
假设有一个简单的 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 列表中的最后一个对象。 |
作用
- 访问根对象: 可以方便地访问通过 QML 加载的主界面对象,通常用于设置属性或调用方法。
- 对象管理: 允许在运行时动态地管理 QML 根对象,例如添加、移除或替换根对象。
- 调试和测试: 在调试和测试过程中,可以通过
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 对象,需要确保以下几点:
- 在 QML 中设置对象的
objectName
属性。 - 使用
findChild
方法进行对象查找时,查找的是objectName
,而不是id
。 - 如果对象不是直接子对象,使用
Qt::FindChildrenRecursively
标志进行递归查找。
这样做可以确保能够在 C++ 代码中准确查找到所需的 QML 对象。