使用 C++ 代码扩展 QML 时,可以使用 QML 类型系统注册 C++ 类,使 C++类能够用作 QML 代码中的数据类型。虽然任何 QObject 派生类的属性、方法和信号都可以从 QML 访问,但只有注册的的类才能用作 QML 的数据类型。
一、使用 QML 类型系统注册 C++ 类型
QObject 派生类可以在 QML 类型系统中注册。
QML引擎允许注册可实例化类型和不可实例化类型:
- 注册可实例化类型使 C++ 类可以用作 QML 对象类型的定义,从而允许在 QML 代码的对象声明中使用它来创建这种类型的对象。注册还为引擎提供了额外的类型元数据,使类型(以及类声明的任何枚举)能够用作在 QML 和 C++ 之间交换的属性值、方法参数和返回值以及信号参数的数据类型。
- 注册不可实例化的类型也会以这种方式将该类注册为数据类型,但该类型不能用于从 QML 实例化为 QML 对象类型。
下面提到的所有宏都可以从 qqmlregistration.h 头文件中获得:
#include <QtQml/qqmlregistration.h>
1.1、注册一个可实例化的对象类型
任何 QObject 派生的 C++ 类都可以注册为 QML 对象类型的定义。一旦在 QML 类型系统中注册了一个类,就可以像 QML 代码中对象类型一样声明和实例化该类。创建后,可以从 QML 操作类实例,任何 QObject 派生类的属性、方法和信号都可以从 QML 代码访问。
要将 QObject 派生类注册为可实例化的 QML 对象类型,请将 QML_ELEMENT 或 QML_NAMED_ELEMENT(<name>) 添加到类声明并将 CONFIG += qmltypes、QML_IMPORT_NAME 和 QML_IMPORT_MAJOR_VERSION 添加到项目文件。这会将类注册到给定主要版本下的类型命名空间中,使用类名或明确给定的名称作为 QML 类型名称。次要版本将从附加到属性、方法或信号的任何修改中派生。默认次要版本为 0。可以通过将 QML_ADDED_IN_MINOR_VERSION() 宏添加到类声明中来明确限制类型只能从特定次要版本中使用。
例如,假设有一个具有 author 和 creationDate 属性的 Message 类:
//头文件
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QObject>
#include <QDateTime>
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QDateTime creationDate READ getCreationDate WRITE setCreationDate RESET resetCreationDate NOTIFY creationDateChanged)
Q_PROPERTY(QString author READ getAuthor WRITE setAuthor RESET resetAuthor NOTIFY authorChanged)
QML_ELEMENT
public:
explicit Message(QObject *parent = nullptr);
const QDateTime &getCreationDate() const;
void setCreationDate(const QDateTime &newCreationDate);
void resetCreationDate();
const QString &getAuthor() const;
void setAuthor(const QString &newAuthor);
void resetAuthor();
signals:
void creationDateChanged();
void authorChanged();
private:
QString author;
QDateTime creationDate;
};
#endif // MESSAGE_H
//源文件
#include "message.h"
Message::Message(QObject *parent) : QObject(parent)
{
}
const QDateTime &Message::getCreationDate() const
{
return creationDate;
}
void Message::setCreationDate(const QDateTime &newCreationDate)
{
if (creationDate == newCreationDate)
return;
creationDate = newCreationDate;
emit creationDateChanged();
}
void Message::resetCreationDate()
{
setCreationDate({}); // TODO: Adapt to use your actual default value
}
const QString &Message::getAuthor() const
{
return author;
}
void Message::setAuthor(const QString &newAuthor)
{
if (author == newAuthor)
return;
author = newAuthor;
emit authorChanged();
}
void Message::resetAuthor()
{
setAuthor({}); // TODO: Adapt to use your actual default value
}
可以通过向项目文件添加适当的类型命名空间和版本号来注册此类型。例如,要使 com.mycompany.messaging 命名空间中的类型可用 1.0 版:
CONFIG += qmltypes
QML_IMPORT_NAME = com.mycompany.messaging
QML_IMPORT_MAJOR_VERSION = 1
这会生成此类的元对象json文件:<工程名称>_metatypes.json
[
{
"classes": [
{
"classInfos": [
{
"name": "QML.Element",
"value": "auto"
}
],
"className": "Message",
"object": true,
"properties": [
{
"constant": false,
"designable": true,
"final": false,
"index": 0,
"name": "creationDate",
"notify": "creationDateChanged",
"read": "getCreationDate",
"required": false,
"reset": "resetCreationDate()",
"scriptable": true,
"stored": true,
"type": "QDateTime",
"user": false,
"write": "setCreationDate"
},
{
"constant": false,
"designable": true,
"final": false,
"index": 1,
"name": "author",
"notify": "authorChanged",
"read": "getAuthor",
"required": false,
"reset": "resetAuthor()",
"scriptable": true,
"stored": true,
"type": "QString",
"user": false,
"write": "setAuthor"
}
],
"qualifiedClassName": "Message",
"signals": [
{
"access": "public",
"name": "creationDateChanged",
"returnType": "void"
},
{
"access": "public",
"name": "authorChanged",
"returnType": "void"
}
],
"superClasses": [
{
"access": "public",
"name": "QObject"
}
]
}
],
"inputFile": "message.h",
"outputRevision": 68
}
]
如果无法从项目的包含路径访问类声明的头文件,可能需要修改包含路径,以便可以编译生成注册代码:
INCLUDEPATH += com/mycompany/messaging
该类型可以在 QML 的对象声明中使用,并且可以读取和写入其属性,如下例所示:
import QtQuick 2.14
import QtQuick.Window 2.14
import com.mycompany.messaging 1.0
Window
{
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Message
{
id:msg
author: "Amelie"
creationDate: new Date()
}
Component.onCompleted: console.log(msg.author)
}
1.2、注册不可实例化的类型
QObject 派生类在某些情况下可能需要在 QML 类型系统中注册,但不是作为可实例化的类型:
- 是不应实例化的接口类型
- 是不需要暴露给 QML 的基类类型
- 声明一些应该可以从 QML 访问的枚举,否则不应该是可实例化的情况
- 是一种应该通过单例实例提供给 QML 的类型,并且不应该从 QML 实例化
Qt QML 模块提供了几个用于注册不可实例化类型的宏:
- QML_ANONYMOUS:注册一个 C++ 类型,该类型不可实例化且不能从 QML 引用。
- QML_INTERFACE:注册一个现有的 Qt 接口类型。该类型不能从 QML 实例化,并且不能用它声明 QML 属性。
- QML_UNCREATABLE(reason) 与 QML_ELEMENT 或 QML_NAMED_ELEMENT 结合注册一个 C++ 类型,该类型不可实例化,但应可识别为 QML 类型系统的类型。适用于应该可以从 QML 访问类型的枚举或附加属性,但类型本身不应该是可实例化的情况。
- QML_SINGLETON 与 QML_ELEMENT 或 QML_NAMED_ELEMENT 结合注册一个可以从 QML 导入的单例类型。
所有在 QML 类型系统中注册的 C++ 类型都必须是 QObject 派生的,即使它们是不可实例化的。
1.2.1、使用单例类型注册单例对象
单例类型允许在命名空间中公开属性、信号和方法,而无需手动实例化对象实例。特别是 QObject 单例类型是提供功能或全局属性值的一种有效且方便的方式。
单例类型没有关联的 QQmlContext,因为它们在引擎中的所有上下文中共享。QObject 单例类型实例由 QQmlEngine 构造和拥有,并会在引擎销毁时销毁。
QObject 单例类型可以以类似于任何其他 QObject 或实例化类型的方式进行交互,除了只存在一个(引擎构造和拥有的)实例,并且必须通过类型名称而不是 id 引用它。
一旦注册,QObject 单例类型即可被导入和使用。
//头文件
#ifndef SINGLETON_H
#define SINGLETON_H
#include <QObject>
class Singleton : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged MEMBER text)
public:
explicit Singleton(QObject *parent = nullptr);
const QString &getText() const;
void setText(const QString &newText);
signals:
void textChanged();
private:
QString text{"改革春风吹满地"};
};
#endif // SINGLETON_H
//源文件
#include "singleton.h"
Singleton::Singleton(QObject *parent) : QObject(parent)
{
}
const QString &Singleton::getText() const
{
return text;
}
void Singleton::setText(const QString &newText)
{
if (text == newText)
return;
text = newText;
emit textChanged();
}
这是一个普通的 C++ 非单例类,可以将它注册为QML中的单例类:
QScopedPointer<Singleton> p(new Singleton);
qmlRegisterSingletonInstance("MySingleton", 1, 0, "TheSingleton_Singleton", p.get());
import QtQuick 2.14
import QtQuick.Window 2.14
import com.mycompany.messaging 1.0
import MySingleton 1.0
Window
{
id:root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Component.onCompleted: console.log(TheSingleton_Singleton.text)
}
QJSValue 也可以作为单例类型公开,但是不能绑定到这种单例类型的属性。
注意:QML 中注册类型的枚举值应以大写开头。
1.3、FINAL属性
使用 Q_PROPERTY 的 FINAL 修饰符声明为 final 的属性不能被覆盖。这意味着 QML 引擎会忽略在 QML 或 C++ 中针对派生类型声明的任何同名属性或函数。
应该尽可能将属性声明为 FINAL,以避免意外覆盖。属性的覆盖不仅在派生类中可见,而且在执行基类上下文的 QML 代码中也是可见的。
声明为 FINAL 的属性也不能被 QML 中的函数或 C++ 中的 Q_INVOKABLE 方法覆盖。
1.4、类型修订和版本
许多类型注册函数需要为注册类型指定版本。类型修订版本允许在新版本中存在新的属性或方法,同时与以前的版本保持兼容。
还是上面的代码:
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ getAuthor WRITE setAuthor RESET resetAuthor NOTIFY authorChanged REVISION 1)
...
}
给属性加上 REVISION 标记,用于将此属性标记为在类型的修订版 1 中添加。
那么在QML中,在1.0中使用此属性就会报错:
import QtQuick 2.14
import QtQuick.Window 2.14
import com.mycompany.messaging 1.0
Window
{
id:root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Message
{
id:msg
author: "Amelie"
creationDate: new Date()
}
Component.onCompleted: console.log(msg.author)
}
导入1.1版本则可正常使用:
import com.mycompany.messaging 1.1
也可以使用 Q_REVISION(x) 宏为修订版标记 Q_INVOKABLE、信号和槽等方法,如:
class CppType : public BaseType
{
Q_OBJECT
Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION 1)
QML_ELEMENT
signals:
Q_REVISION(1) void rootChanged();
};
出于同样的原因,在更高版本中引入的新类型应该使用 QML_ADDED_IN_MINOR_VERSION 宏进行标记。
该语言的这一特性允许在不破坏现有应用程序的情况下进行行为更改。
注意:QML 引擎不支持对分组和附加属性对象的属性或信号进行修订。
1.5、注册扩展对象
将现有的类和集成到 QML 中时,API 通常需要进行调整以更好地适应声明式环境。 虽然最好的结果通常是通过直接修改原始类来获得,但如果这是不可能的或者由于其他一些问题而变得复杂,扩展对象允许有限的扩展可能性而无需直接修改类代码。
扩展对象向现有类型添加附加属性。扩展对象只能添加属性,不能添加信号或方法。扩展类型定义允许程序员在注册类时提供附加类型。 在 QML 中使用时,这些属性会与原始目标类合并。
例如:
QLineEdit
{
leftMargin: 20
}
leftMargin 属性是添加到现有 C++ 类型 QLineEdit 的新属性,无需修改其源代码。
QML_EXTENDED(extended) 宏用于注册扩展类型。参数是要用作扩展的另一个类的名称。
还可以使用 QML_EXTENDED_NAMESPACE(namespace) 来注册命名空间,尤其是其中声明的枚举,作为类型的扩展。
扩展类是一个普通的 QObject,带有一个接受 QObject 指针的构造函数。 但是,扩展类的创建会延迟到访问第一个扩展属性时。 扩展类被创建,目标对象作为父对象传入。 当访问原始属性时,将使用扩展对象上的相应属性。
详见QQmlEngine中的宏成员。
1.6、注册外部类型
某些无法修改的 C++ 类型可能是来自 第三方库,可以使用 QML_FOREIGN 宏将这些类型公开给 QML。 为此,创建一个完全由注册宏组成的单独结构,如下所示:
#include <3rdpartyheader.h>
struct Foreign
{
Q_GADGET
QML_FOREIGN(Immutable3rdParty)
QML_NAMED_ELEMENT(Accessible3rdParty)
QML_ADDED_IN_VERSION(2, 4)
// QML_EXTENDED, QML_SINGLETON ...
};
这段代码中,将注册一个 QML 类型,该类型具有 Immutable3rdParty 的方法和属性,以及 Foreign 中指定的 QML 特征(例如:单例、扩展)。
二、定义 QML 特定的类型和属性
2.1、提供附加属性
在 QML 语法中,有附加属性和附加信号处理程序的概念。本质上,这些属性是由一个附加类型实现和提供的,这些属性可以附加到另一个类型的对象上。
例如,下面的 Item 使用附加属性和附加处理程序:
import QtQuick 2.0
Item
{
width: 100; height: 100
focus: true
Keys.enabled: false
Keys.onReturnPressed: console.log("Return key was pressed")
}
在这里,Item 对象能够访问和设置 Keys.enabled 和 Keys.onReturnPressed 的值。这允许 Item 对象访问这些额外的属性,作为其自身现有属性的扩展。
2.1.1、实现附加对象的步骤
上述示例中:
- 有一个匿名附加对象,该对象带有一个 enabled 属性和一个 returnPressed 信号,Item 对象能够访问和设置这些属性。
- Keys 是附加类型,它为附加对象提供了一个命名限定符“Keys”,通过它可以访问匿名附加对象类型的属性。
通过为附加对象类型和附加类型提供类,可以从 C++ 实现提供附加对象的机制:
- 对于附加对象类型,需要是 QObject 派生类,该类定义了附加对象可访问的属性。
- 对于附加类型,需要是 QObject 派生类。
qmlAttachedProperties()返回附加对象类型的实例:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);
QML 引擎调用qmlAttachedProperties()将附加对象类型的实例附加到由 object 参数指定的附加对象。一般将返回的实例作为 object 的父对象,以防止内存泄漏。
该方法最多由引擎为每个附加对象实例调用一次,因为引擎缓存返回的实例指针以供后续附加属性访问。 因此,在附件对象被销毁之前,附加对象可能不会被删除。
通过将 QML_ATTACHED(attached) 宏添加到类声明中,将其声明为附加类型。参数是附加对象类型的名称。
2.1.2、实现附加对象的示例
使用上面描述的 Message 类型。假设需要在消息发布到留言板时在消息上触发信号,并且还需要在留言板上跟踪消息何时过期。由于将这些属性直接添加到 Message 没有意义,因为这些属性与留言板上下文更相关,因此它们可以作为 Message 对象上的附加属性来实现。
#ifndef MESSAGEBOARDATTACHEDTYPE_H
#define MESSAGEBOARDATTACHEDTYPE_H
#include <QObject>
#include <QtQml/qqmlregistration.h>
#include <QQmlEngine>
//附加对象类型
class MessageBoardAttachedType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool expired READ getExpired WRITE setExpired NOTIFY expiredChanged)
QML_ANONYMOUS
public:
explicit MessageBoardAttachedType(QObject *parent = nullptr);
bool getExpired() const;
void setExpired(bool newExpired);
signals:
void published();//消息已发布
void expiredChanged();
private:
bool expired;//消息是否过期
};
//附加类型
class MessageBoard : public QObject
{
Q_OBJECT
QML_ATTACHED(MessageBoardAttachedType)
QML_ELEMENT
public:
static MessageBoardAttachedType *qmlAttachedProperties(QObject *object)
{
return new MessageBoardAttachedType(object);
}
};
#endif // MESSAGEBOARDATTACHEDTYPE_H
现在, Message 类型可以访问附加对象类型的属性和信号:
import QtQuick 2.14
import QtQuick.Window 2.14
import com.mycompany.messaging 1.0
Window
{
id:root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Message
{
id:msg
author: "张三"
creationDate: new Date()
}
MessageBoard.expired: true
MessageBoard.onPublished: console.log(author + "发布了消息")
Component.onCompleted: console.log(root.MessageBoard.expired)
}
此外,C++ 实现可以通过调用 qmlAttachedPropertiesObject() 函数访问已附加到任何对象的附加对象实例。如:
Message *msg = someMessageInstance();
MessageBoardAttachedType *attached = qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg));
qDebug() << "MessageBoard.expired:" << attached->expired();
2.2、属性修饰符类型
属性修饰符类型是一种特殊的 QML 对象类型,属性修饰符类型实例影响它所应用的属性。
有两种不同的属性修饰符类型:
- 属性值写入拦截器:可用于过滤或修改写入属性的值。 目前,唯一支持的属性值写入拦截器是 Behavior 类型。
- 属性值源:可用于随时间自动更新属性值。可以定义自己的属性值源类型。各种属性动画类型就是属性值源。
可以通过“<ModifierType> on <propertyName>”语法创建属性修饰符类型实例并将其应用于 QML 对象的属性,如下例所示:
import QtQuick 2.0
Item {
width: 400
height: 50
Rectangle {
width: 50
height: 50
color: "red"
NumberAnimation on x {
from: 0
to: 350
loops: Animation.Infinite
duration: 2000
}
}
}
这通常称为“on”语法。
2.2.1、自定义属性值源
通过继承 QQmlPropertyValueSource 并提供随时间将不同值写入属性的实现,可以在 C++ 中实现属性值源。当将属性值源应用于属性时,引擎会为其提供此属性的引用,即可更新属性值。
例如,假设有一个 RandomNumberGenerator 类可用作属性值源,当应用于 QML 属性时,它将每 500 毫秒将属性值更新为不同的随机数。这个类可以实现如下:
#ifndef RANDOMNUMBERGENERATOR_H
#define RANDOMNUMBERGENERATOR_H
#include <QObject>
#include <QQmlPropertyValueSource>
#include <QtQml/qqmlregistration.h>
#include <QTimer>
#include <QQmlProperty>
class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource
{
Q_OBJECT
Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged)
Q_INTERFACES(QQmlPropertyValueSource)
QML_ELEMENT
public:
explicit RandomNumberGenerator(QObject *parent = nullptr);
int maxValue() const;
void setMaxValue(int newMaxValue);
virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; }
signals:
void maxValueChanged();
private:
void updateProperty();
QQmlProperty m_targetProperty;
QTimer m_timer;
int m_maxValue{100};
};
#endif // RANDOMNUMBERGENERATOR_H
#include "randomnumbergenerator.h"
#include <QRandomGenerator>
RandomNumberGenerator::RandomNumberGenerator(QObject *parent) : QObject(parent)
{
QObject::connect(&m_timer, &QTimer::timeout, this, &RandomNumberGenerator::updateProperty);
m_timer.start(500);
}
int RandomNumberGenerator::maxValue() const
{
return m_maxValue;
}
void RandomNumberGenerator::setMaxValue(int newMaxValue)
{
if (m_maxValue == newMaxValue)
return;
m_maxValue = newMaxValue;
emit maxValueChanged();
}
void RandomNumberGenerator::updateProperty()
{
m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue));
}
当 QML 引擎遇到使用 RandomNumberGenerator 作为属性值源时,它会调用 RandomNumberGenerator::setTarget() 为类型提供值源已应用到的属性。 当 RandomNumberGenerator 中的内部计时器每 500 毫秒触发一次时,它将向该指定属性写入一个新的数值。
一旦 RandomNumberGenerator 类在 QML 类型系统中注册,它就可以从 QML 中用作属性值源。
示例,每隔 500 毫秒更改窗口的宽度:
import QtQuick 2.14
import QtQuick.Window 2.14
import com.mycompany.messaging 1.0
Window
{
id:root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
RandomNumberGenerator on width { maxValue: 900 }
}
在其他方面,属性值源是常规 QML 类型,可以具有属性、信号方法等。
当一个属性值源对象被分配给一个属性时,QML 首先尝试正常分配它,就好像它是一个常规的 QML 类型。 仅当此分配失败时,引擎才会调用 setTarget() 方法。
2.3、为 QML 对象类型指定默认和父属性
任何注册为可实例化 QML 对象类型的 QObject 派生类型都可以选择为该类型指定默认属性。 默认属性是在对象的子项未分配给任何特定属性时自动分配到的属性。
可以通过为具有特定“DefaultProperty”值的类调用 Q_CLASSINFO() 宏来设置默认属性。
例如,下面的 MessageBoard 类将其 messages 属性指定为该类的默认属性:
class MessageBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
Q_CLASSINFO("DefaultProperty", "messages")
QML_ELEMENT
public:
QQmlListProperty<Message> messages();
private:
QList<Message *> m_messages;
};
这使得 MessageBoard 对象的子级可以在未分配给特定属性的情况下自动分配给其 messages 属性。 例如:
MessageBoard
{
Message { author: "Naomi" }
Message { author: "Clancy" }
}
如果没有将消息设置为默认属性,则必须将任何消息对象显式分配给消息属性,如下所示:
MessageBoard
{
messages:
[
Message { author: "Naomi" },
Message { author: "Clancy" }
]
}
注意:Item::data 属性是它的默认属性。添加到此 data 属性的任何 Item 对象也会添加到 Item::children 列表中,因此使用默认属性可以为项目声明可视子项,没有明确地将它们分配给 children 属性。
此外,可以使用Q_CLASSINFO()声明“ParentProperty”值以通知 QML 引擎哪个属性应表示 QML 层次结构中的父对象。例如,消息类型可能声明如下:
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* board READ board BINDABLE boardBindable)
Q_PROPERTY(QString author READ author BINDABLE authorBindable)
Q_CLASSINFO("ParentProperty", "board")
QML_ELEMENT
public:
Message(QObject *parent = nullptr) : QObject(parent) { m_board = parent; }
QObject *board() const { return m_board.value(); }
QBindable<QObject *> boardBindable() { return QBindable<QObject *>(&m_board); }
QString author() const { return m_author.value(); }
QBindable<QString> authorBindable() { return QBindable<QString>(&m_author); }
private:
QProperty<QObject *> m_board;
QProperty<QString> m_author;
};
2.4、使用 Qt Quick 模块定义可视项
使用 Qt Quick 模块构建用户界面时,所有要可视化呈现的 QML 对象都必须派生自 Item 类型,因为它是 Qt Quick 中所有可视对象的基本类型。该 Item 类型由 Qt Quick 模块提供的 QQuickItem C++ 类实现。 因此,当需要在 C++ 中实现可以集成到基于 QML 的用户界面中的可视类型时,应该对此类进行子类化。
三、接收对象初始化通知
对于某些自定义 QML 对象类型,某些情况可能需要延迟特定数据的初始化直到创建对象并设置其所有属性。例如初始化成本高,或者在所有属性值都已初始化之前不应执行初始化等情况。
Qt QML 模块提供的 QQmlParserStatus 类用于此目的。它定义了许多在组件实例化期间的各个阶段调用的虚拟方法。 要接收这些通知,C++ 类应该继承 QQmlParserStatus 并使用 Q_INTERFACES() 宏通知 Qt 元系统。
例如:
class MyQmlType : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
QML_ELEMENT
public:
void classBegin()
{
//QML中本类型对象创建完成,但是任何属性没有被赋值,可执行自定义操作
}
virtual void componentComplete()
{
//QML中对象已经完全创建,可执行自定义操作
}
};