前言
QML 是一套 QT 开发前端显示的软件,其最大特色就是基于 Qt 的宏系统实现了与 C++ 及 Javascript 交互和连接,本篇主要讲解 QML 与 C++ 的交互( QML 调用 C++),至于 C++ 调用 QML 部分相对比较简单查看官方文档即可
很多 C++ 与 QML 以及 Javascript 的变量,宏系统会自动进行切换
其他内容:
Javascript 动态生成 QML 对象
QML Chart 使用
QML 动画效果讲解
目录结构
官方文档 Official Documentation If you need English documentation, you can check the official documentation
一、信号和槽
主要简单介绍了Qt信号
二、QML调用 C++ 的各种办法以及要完成调用的过程
注意:只要了解 1 和 2 就已经掌握了 QML 与 C++ 交互的关键
1. C++ 类注册 & QML 加载方式 & 不同注册的区别
2. 各种调用方式
1) 信号与槽
2) C++ 定义可被调用的属性
3) QML 中实例化 C++ 子类(一个或多个,以及一种宏定义)
4) 以结构体方式调用子类
5) 定时器方式定时动态更新变量值
6) QML 下控制 C++ 实例化先后顺序
7) 版本控制
8) 为 C++ 拓展附属性
补充
1)QML 自动绑定
Item {
property var val : 30
Text {
text: val
}
}
//例 2
Item {
property var val : 30
Text {
id: t1
text: val
}
Component.onCompleted: {
t1.text = 1
}
}
//例 3
Item {
property var val : 30
Text {
id: t1
}
Binding {
target: t1
property: "text"
value: val
restoreMode: Binding.RestoreBinding
}
Component.onCompleted: {
t1.text = 1
}
}
这样 Text 的 属性 text 就自动与 val 变量绑定在一起了,但是如果执行 例2 操作之后,就会自动解绑,那这种时候该怎么弄?
使用 Binding 如 例3 就可以确保绑定不会断开,其中 restoreMode 有好几种模式可以自行查看
一、信号和槽
类似信号通讯处理机制,一方发出信号,指定一个或多个接收方用于处理任务,是异步的
如果不了解常见的信号通讯处理机制,可以理解为在程序的之上还有一层处理机制,信号和槽对应的函数均提交到顶层数组之中等待触发和调配。
满足以下条件既可以用
1 object 子类
2 注明 Q_OBJECT
3 信号用 signals: 槽用 private/publice slots:
4 连接 connect();
除了能够处理异步通信之外,最大优点就是可以确保线程安全
例程 xxx.h
#include <QObject>
#include <QDebug> //Qt 特有的打印调试信息工具
class Test : public QObject
{
Q_OBJECT
public:
explicit Test() {
// connect(this, SIGNAL(send2Target()), this, SLOT(rece2Signal())); // Qt4 写法 this --> 可替换对应 实例化变量的指针
connect(this, &Test::send2Target, this, &Test::rece2Signal);
qDebug() << "I sent out a signal";
emit send2Target(); //emit 是触发信号的方式,这是比较标准的写法,让阅读者可以理解这是一个信号,而非处理函数
// send2Target(); // 也可以发送信号,但是不建议如此,难以区分函数的类型和作用
private slots: //槽 public slots / pravite slots 均可,作用范围取决于前缀
void rece2Signal() {
qDebug() << "I got a signal";
}
};
二、 QML 调用 C++ 类的各种方法
注意:QML 使用的几个注意事项
1、QML 文件必须以类名形式命名文件,首字母大写 如 Test.qml
2、QML 文件相互之间不需要通过 import 导入,前提在同一目录(非同一目录需要 import “目录名称”)
3、QML 文件需要导入 xxx.qrc 资源文件件统一管理较为合适(取决于启动方式)
- QUrl::fromLocalFile(“xxx.qml”); 直接去寻找本地目录
- QUrl(QStringLiteral(“qrc:/xxx.qml”);//检索资源目录
- 其他方式请翻看官方文档说明
4、比如在 Test1.qml 中调用 同目录下的 Test2.qml ,只需要 Test2 { } 即可实例化,与文件名一致(别名等其他请翻看官方说明)
第一步:注册 / 实例化 (包括 2 种 C++ 启动 QML 方法)####
QML 需要通过 QML 引擎加载并实现
1. 通过自主实例化后,再加载
例程 xxx.cpp //如果提示模块找不到 请在 .pro 文件 Qt += qml quick 添加模块
#include <QQuickView>
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlContext>
#include "test.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
Test test;
/***************** 老版本方式 *************************/
// QQuickView view; // C++ 启动 QML 方式一
// view.engine()->rootContext()->setContextPrroperty("MyTest", &test);
// view.setSource(QUrl(QStringLiteral("qrc:/main.qml"))); //main.qml 已经提前添加到 xxx.qrc QT 特有的资源文件中[百度 Qt 创建资源文件]
// view.show();
/**************** 新版本方式 ************************/
QQmlApplicationEngine engine; // C++ 启动 QML 方式一
engine.engine()->rootContext()->setContextPrroperty("MyTest", &test);
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); //main.qml 已经提前添加到 xxx.qrc QT 特有的资源文件中[百度 Qt 创建资源文件]
return app.exec();
}
/***************************main.qml**********************************************?
import QtQuick 2.13
Item {
visible: true
width: 200
height: 200
Component.onCompleted: {
MyTest.send2Target()
MyTest.rece2Signal() //注意:必须 public slots 或者 Q_INVOKEABLE 函数(后面细说)
}
}
2. 通过注册器注册
例程 xxx.Cpp //QML 启动方式二 在创建 QML 类型工程时自动生成的
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "test.h"
int main(int argc, char *argv[])
{
qmlRegisterType<Test>("TestType", 1, 0, "Test");// C++ 类名,主版本号,次版本号,QML 调用类名
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine; //C++ 启动 QML 方式二 (官方推荐)
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection); // Qt::QueuedConnection 是 connect 的连接方式,其他方式翻看官方文档
engine.load(url);
return app.exec();
}
/***************************** main.qml ********************************/
import QtQuick 2.13
import TestType 1.0
Item {
visible: true
width: 200
height: 200
Test {
id: testItem
Component.onCompleted: {
send2Target()
rece2Signal() //注意:必须 public slots 或者 Q_INVOKEABLE 函数(后面细说)
}
/**************** 如下也行 *****************/
Component.onCompleted: {
testItem.send2Target()
testItem.rece2Signal()
}
}
说明一: QML 能够调用的 C++ 元素必须是声明到宏中 比如通过注册器进行声明
说明二: 注册器类别
注册器分为://更多请查看官方文档
1、实例化注册:(可在 QML 实例化并调用)
通常使用 qmlRegisterType(“TestType”, 1, 0, “Test”);
2、声明注册
1)qmlRegisterType() //可以让继承 Test 的子类可以被实例化(用于声明父类)
2)qmlRegisterInterface(“Test”) //声明接口,当有子类被分配到直接接口,QML 会自动通过接口去调用
3)qmlRegisterUncreatableType(“Test”, 1, 0, “Test”, “can not instantiable”); //主要用于声明:附属属性和枚举(enum)的类
4)qmlRegisterSingletonType(“Test”, 1, 0, “Test”,singletontype_provider) //只允许实例化一次
singleton 需要定义的 //详见官方文档
static QJSValue singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) {
Q_UNUSED(engine)
static int seedValue = 5;
QJSValue example = scriptEngine -> newObject();
example.setProperty("Test", seedValue++);
return example;
}
除以上介绍之外还有很多其他类型,可参靠官方说明
比如某些条件不允许实例化的 qmlRegisterTypeNotAvailable()
第二步、实现各种可别调用的类型
1. 信号和槽
在 QML 中定义信号 signal clickButton();
默认会有一个槽是 onClickButton 自动与之相关联
file: main.qml
import QtQuick 2.13
import QtQuick.Window 2.12
Window {
id: window
visible: true
width: 200
height: 200
title: qsTr("hello world") //qsTr 是为了后续本地化做服务,类似 Qt 下的 tr("")
signal clickForTest()
Text {
id:testText
text: qsTr("text")
}
MouseArea {
anchors.fill: parent
onClicked: clickForTest()
}
onClickForTest: testText.text = "OK";
}
//点击窗口可以查看到 text 变成 OK 说明成功
同理如果在 C++ 类中声明的 信号 和 槽 在类实例化后也可以同样这么使用
以两种槽才可被 QML 调用
1 使用 public slots: 关键字下声明
2 在 public 关键字下声明,并且在函数之前 Q_INVOKABLE void testSlot();
file: test.h
#include <QDebug>
#include <QObject>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test() {}
Q_INVOKABLE void testSlot1() { qDebug() << "I am testSlot1";} //等价在 public slots: 下声明
public slots:
void testSlot2() { qDebug() << "I am testSlot2";}
signals:
void testSignal();
};
/*------------------------------file: main.cpp--------------------------------------------------*/
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlContext>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[] ) {
qmlRegisterType<Test>("Test", 1, 0, "Test");
QGuiApplication app(argc, argv):
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml");
engine.load(url);
return app.exec();
}
/*--------------------------------file: main.qml ----------------------------*/
import QtQuick 2.13
import QtQuick.Window 2.12
import Test 1.0
Window {
id: window
visible: true
width: 200
height: 200
title: qsTr("hello, world!")
Test {
id: test
onTestSignal: consloe.log(" I am testSignal")
}
MouseArea {
anchors.fill: parent
onClicked: {
test.testSignal() //发送信号
test.testSlot2() //直接调用函数 类似 QML 发送testSlot2() 信号到 onTestSlot2() 一样
test.testSlot1() // 同上,只是为了验证 Q_INVOKABLE 的效果
}
}
}
以上就是,信号和槽在 QML 的调用办法,接下来讲一下在 QML 中如何绑定特定信号与槽
- 使用 javascript 环境下使用 .connect / .disconnect 连接 / 断开
- 使用 Connections 框架
优势:
Connecting to targets not defined in QML 也就是可以连接没有定义在当前 QML 的对象信号
QML 中槽也就是执行函数,就是 JavaScript 函数
/*
方法一: 使用 .connect
file: main.qml
分别演示:
1 同范围
2 不同范围
3 一信号对多个槽
4 与 C ++ 信号 及 槽连接
*/
import QtQuick 2.13
import QtQuick.Window 2.12
import Test 1.0
Window {
id: window
visible: true
width: 500
height: 500
title: qsTr("hello world !");
signal send2Slot1()
Test {
id: test
onSend2Target: consloe.log("I got a signal from send2Target"); //QML 默认信号连接
}
MouseArea {
signal send2Slot2()
id: mouseTest
anchors.fill: parent
onClicked: {
send1Slot1()
test.send2Target()
send2Slot2()
}
}
//Component.onCompleted 能够提供完整的 javascript 运行环境
Component.onCompleted: {
mouseTest.send2Slot2.connect(test.testSlot2); // QML 信号 连接 C++ 槽
mouseTest.send2Slot2.connect(testSlot3); //不同定义范围连接
send2Slot1.connect(test.testSlot1);
send2Slot1.connect(testSlot4); //一个信号对多个槽
test.send2Target.connect(testSlot3); // C++ 信号 连接 QML 槽
}
function testSlot3() {
consloe.log("I am testSlot3");
}
function testSlot4() {
consloe.log("I an testSlot4");
}
}
/*
方法二: Connections ( If you need some properties, the function needs to import QtQml 2.3)
类似 enabled 属性需要导入 QtQml 2.3 否则默认 enabled 为导通,如不需要这类属性可不导入 QtQml 2.3
特性与 方法一类型,不作另外说明和演示,只对其最大优势项进行说明
*/
/*------ main.qml ------*/
import QtQuick 2.13
import QtQuick.Window 2.12
import "subDir" //导入子目录,演示位于不同目录该怎么调用,记住 QML 文件首字母必须大写
Window {
id: window
visible: true
width: 500
height: 500
title: qsTr("Hello world")
signal send2Slot2()
MouseArea {
anchors.fill: parent
onClicked: {
send2Slot2()
}
}
Test1 { //这样就实例化 Test1
}
}
/*------------------ subDir/Test1.qml --------------------*/
import QtQuick 2.0
Item {
Test2 { //同一目录则不需要特别说明
}
}
/*------------------ subDir/Test2.qml -------------------*/
import QtQuick 2.0
Item {
Connections {
target: window //信号来源
onSend2Slot1: {
consloe.log("I got a signal from window");
}
enabled: true // true or false, 使用这个属性需要导入 import QtQml 2.3 更多请查看官方文档
}
}
以上是信号与槽相关的内容及绑定实例
2、在 C++ 中定义一个属性
一个标准的 QML 属性应该执行 3 种操作:读、写、通知引用方同步修改
继承于 QObject
通过 Q_PROPERTY() 声明
在这里大家可以看出来,所有 C++ 定义的东西要想被 QML 调用就必须声明并且提交到 宏系统 之中,通过借助一些宏定义函数就能够实现这些功能,比如枚举 Q_ENUM() / Q_CLASSINFO() / Q_ENUM_NS() / Q_DECLARE_METATYPE()
具体如下
// file: piechart.h
#include <QObject>
#include <QQuickPaintedItem>
class PieChart : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
explicit PieChart(QQuickItem *parent = nullptr); // 老版本支持使用 NULL / 0,规范是 nullptr
~PieChart();
QString name() const;
void setName(const QString &name);
signals:
void nameChanged();
private:
QString m_name;
};
//file: piechart.cpp
#include "piechart.h"
#include <QPainter>
PieChart::PieChart(QQuickItem *parent) : QQuickPaintedItem (parent) {}
PieChart::~PieChart() {}
QString PieCharts::name() const {
return m_name;
}
void PieCharts::setName(const QString &name) {
if (name == m_name) //这步很重要,按照官方说明可以避免不必要的修改和避免进入死循环
return;
m_name = name;
emit nameChanged();
}
这个是 Q_PROPERTY 的函数说明 官方英文说明 // 他人中文说明
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
简单说明:
(QString name READ name WRITE setName NOTIFY nameChanged) 官方推荐命名方式
QString name // 类命 属性名 //属性名:在 QML 使用时的表示
READ name // 返回 name 值的函数
WRITE setName //修改 name 值的函数
NOTIFY nameChanged //通知 QML 修改显示的信号,通常放在 setName 函数内
伪代码 在 QML 种调用
PieChart {
name: "xxxx"
}
这边的属性不一定都需要,如果只读或者 QML 不存在引用这个变量的情况,都可以把 WRITE 和 NOTIFY 去掉
至此,基本能够自如的操作 QML 和 C++ 之间的交互,下面内容着重介绍一些特殊的调用方法
3、调用子类 直接在 C++ 实例化子类
直接上代码就一目了然(此为官方代码的完善版,官方只有简介)
方式一: 一次只能创建或调用一个
/************** PieChart **************8/
//file: PieChart.h
#include <QObject>
#include <QQuickPaintedItem>
#include "pieslice.h"
#include <QPen>
class PieChart : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(PieSlice* pieSlice READ pieSlice WRITE setPieSlice )
public:
explicit PieChart(QQuickItem *parent = nullptr):QQuickPaintedItem(parent){ }
QString name() const { return m_name;}
void setName(const QString &name) {
if (name != m_name) {
m_name = name;
emit nameChanged();
}
}
QColor color() const { return m_color; }
void setColor(const QColor &color) {
if (color != m_color) {
m_color = color;
emit colorChanged();
}
}
void paint(QPainter *painter) {
QPen pen(m_color, 2);
painter->setpen(pen);
painter->setRenderHints(QPainter::Antialiasing, true); //抗锯齿
painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16);
}
PieSlice *pieSlice() const { return m_pieSlice; }
void setPieSlice(PieSlice *pieSlice) {
if (pieSlice == nullptr)
return;
m_pieSlice = pieSlice;
pieSlice->setParentItem(this);
}
signals:
void nameChanged();
void colorChanged();
private:
QString m_name;
QColor m_color;
PieSlice *m_pieSlice;
};
//file: pieslice.h
#include <QObject>
#include <QQuickPaintedItem>
#include <QPen>
class PieSlice: public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor)
public:
explicit PieSlice(QQuick *parent = nullptr):QQuickPaintedItem(parent) {}
QColor color() const;
void setColor(const QColor &color) {
if (color == m_color)
return;
m_color = color;
}
void paint(QPainter *painter) {
QPen pen(m_color, 2);
painter->setPen(pen);
painter->setRenderHints(QPainter::Antialiasing, true);
painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90*16, 290*16);
}
private:
QColor m_color;
};
//file: main.cpp
qmlRegisterType<PieSlice>("Charts", 1, 0, "PieSlice");
qmlRegisterType<PieChart>("Charts", 1, 0, "PieCharts");
//QML
import QtQuick 2.13
import Charts 1.0
Item {
width: 200
height: 200
visible: true
}
PieChart {
anchors.centerIn: parent
width: 100; height: 100
pieSlice: PieSlice {
width:10; height: 10;
color: "red"
}
}
方式二: 一次性创建多个
注意:基于方式一程序修改, “+” 表示新增语句
//file: PieChart //QQmlListProPerty 详情请查看官方文档
+ Q_PROPERTY(QQmlListProperty<PieSlice> slices READ slices)
pulic:
+ QQmlListProperty<PieSlice> slices() {
return QQmlListProperty<PieSlice>(this, nullptr, &PieChart::append_slice, nullptr, nullptr, nullptr);
//分别代表 objcete, data, 新增函数,返回总数函数,检索函数,清空函数
}
private:
+ static void append_slice(QQmlListProperty<PieSlice> *list, PieSlice *slice) {
PieChart *chart = qobject_cast<PieChart *>(list -> object); //列表所在的object是固定的也就是 PieChart 的标号,所以直接转换即可得到对应的标识
if (chart) {
slice -> setParentItem(chart);
chart->m_slices.append(slice);
}
}
+ QList<PieSlice *> m_slices;
//file:QML 中 PieChart 改为如下
PieChart {
anchors.centerIn: parent
width: 100; height: 100;
slices: [
PieSlice {
width:10; height: 10
color: "blue"
},
PieSlice {
x: 10;
width: 10; height: 10;
color: "green"
}
]
}
拓展: 如果声明中增加如下语句,方式二中 QML 写法可以修改
//file: piechart.cpp
+ Q_CLASSINFO("DefaultProperty", "slices") //注意:DefalutPeroperty 是关键字不可改,而 slices 则与上文代码声明一致
//file: qml 可以改为
PieChart {
anchors.centerIn: parent
width: 100; height: 100;
PieSlice {
width:10; height: 10
color: "blue"
}
PieSlice {
x: 10;
width: 10; height: 10;
color: "green"
}
}
//这部分代码等价与 方式二,但是如果没有声明 Q_CLASSINFO() 这样写,对程序而言,它会独立创建 2 个 PieSlice 但不属于同一个
PieChart 的子类,可以通常 debug 调试测试,不会条用 append_slice 这个函数
4、以结构体的形式修改子类变量
实现如下:
xxx.name : “dxxx”
xxx.email : “dddd”
代码跟 chapter 3 差不多,前提已经实例化一个子类罢了
//file: mymessage.h
#include <QObject>
#include "messageauthor.h"
class MyMessage: public QObject
{
Q_OBJECT
Q_PROPERTY(MessageAuthor *author READ author WRITE setAuthor NOTIFY authorChanged)
public:
explicit MyMessage(QObject *parent = nullptr): QObject(parent), m_author(new MessageAuthor(this))
{}
~MyMessage() {delete m_author;}
MessageAuthor *author() const () { return m_author; }
void setAuthor( MessageAuthor *author) {
m_author-> setName(author->name());
m_author-> setEmail(author->email());
emit authorChanged();
}
signals:
void authorChanged();
private:
MessageAuthor *m_author;
};
//file: messageauthor.h
#include <QObject>
class MessageAuthor: public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged())
Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged())
public:
explicit MessageAuthor(QObject *parent = nullptr):QObject(parent) {}
QString name () const { return m_name;}
QString email() const { return m_email;}
void setName(const QString &name) {
if (name == m_name)
return ;
m_name = name;
emit nameChanged();
}
void setEmail (const QString &email) {
if (email == m_email)
return;
m_email = email;
emit emailChanged()'
}
signals:
void nameChanged();
void emailChanged();
private:
QString m_name;
QString m_email;
};
//file:main.cpp
qmlRegisterType<MyMessage>("MyMessage", 1, 0, "MyMessage");
qmlRegisterType<MessageAuthor>(); //声明方式一
qmlRegisterUncreatableType<MessageAuthor>("MyMessage", 1, 0, "MessageAuthor", "can't create"); //声明方式二
//file: main.qml
import QtQuick 2.13
import MyMessage 1.0
Item {
width:100; heigth: 100;
visible: true;
MyMessage {
author.name: "testName"
author.email: "testName@qq.com"
}
}
5、通过定时器等方式定时动态更新
官方介绍中有两种方式
1 property value write interceptors 即通过 (Behavior 私有类)interceptor 去修改属性值(此方法只能用于 Behavior 类)
2 property value sources 适用于 animation 类这里只讲解一下第二种方式
条件一:必须是 QObject 和 QQmlPropertyValueSource 子类
条件二:语法必须是 on
条件三:实现 setTarget() 函数
#include <QObject>
#include <QTimer>
class MyMessage : public QObject, public QQmlPropertyValueSource
{
Q_QOBJECT
Q_INTERFACES(QQmlPropertyValueSource)
Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged)
public:
explicit MyMessage(QObject *parent = nullptr) : QObject(parent) , m_maxValue(300){
connect(&m_timer, &QTimer::timeout, this, &MyMessage::updateProperty);
m_timer.start(500);
}
int maxValue() const { return m_maxValue;}
void setMaxValue( const int &maxValue) {
if (maxValue == m_maxValue)
return;
m_maxValue = maxValue;
emit maxValueChanged();
}
virtual void setTarget(const QQmlProperty &prop) {
if (prop == m_targetProperty)
return;
m_targetProperty = prop
};
signals:
void maxValueChanged();
private slots:
void updateProperty() {
m_targetProperty.write(m_maxValue ++);
}
private:
int m_maxValue;
QTimer m_timer;
QQmlProperty m_targetProperty;
};
//file: main.cpp
qmlRegisterType<MyMessage>("MyMessage", 1, 0, "MyMessage");
//file: main.qml
import QtQuick 2.13
import MyMessage 1.0
Item {
visible: true
width: 100; height: 100;
Rectangle {
height: 40
MyMessage on width { maxValue: 30}
color: "blue"
}
}
6、C++ 部分功能延迟完成(所有属性值设定完成之后再进行部分初始化)
此方法仅 QML 环境下可用
条件一:QObject 和 QQmlParserStatus 子类
条件二:实现 componentComplete() 和 classBegin() 具体说明看文档
进行时序:先进行 MyMessage() { } 里的内容,再进行 classBegin() {} 里的内容,最后执行 componentComplete(){} 内容
#include <QObject>
#include <QQmlParserStatus>
#include <QDebug>
class MyMessage : public QObject, public QQmlParserStatus
{
Q_QOBJECT
Q_INTERFACES(QQmlParserStatus)
public:
MyMessage() { qDebug() << "I am message";}
void componentComplete() { qDebug() << " I am component";}
void classBegin() {qDebug() << " I am classBegin" ;}
};
7、QML 版本控制
使用 Q_REVISION()
//伪代码
//file: MyMessage.h
Q_PROPERTY(QString name READ name WRITE setName REVISION 1)
Q_REVISION(1) void setName();
//file:main.cpp
qmlRegister<MyMessage, 1>("MyMessage", 1, 1, "MyMessage");
//file:main.qml
import MyMessage 1.1
8、副属性 attached objects
简而言之,就是能够让 QML 调用在某个 C++ 实例化的子类,与上面几种情况类似
条件一: QObject 子类
条件二:子类有在宏系统声明的函数
条件三:实现 static *qmlAttachedProperies(QObject *object)
条件四:QML_DECLARE_TYPEINFO() 声明 Flag: QML_HAS_ATTACHED_PROPERTIES
伪代码
file:parent.h 假设子类是 MessageAuthor (直接参考上文出现过的 MessageAuthor.h)
#include <QtQml>
class MyMessage : public QObject
{
Q_OBJECT
public:
MyMessage(QObject *parent = nullptr) : QObject(parent){}
static MessageAuthor *qmlAttachedProperties(QObject *object) {
return new MessageAuthor(object);
}
};
QML_DECLARE_TYPEINFO(MyMessage, QML_HAS_ATTACHED_PROPERTIES)
file:main.cpp
qmlRegisterType<MyMessage>("MyMessage", 1, 0, "MyMessage");
qmlRegisterType<MessageAuthor>(); //声明一下
file:main.cpp
import QtQuick 2.13
import MyMessage 1.0
Item {
Rectangle {
MyMessage.name: "ddd"
MyMessage.email: "ddd@qq.com"
}
}
总结:
以上便是关于如何创建在 QML 中调用 C++ 类的方法