您现在的位置是:首页 >技术交流 >c++类型注册到QML中网站首页技术交流
c++类型注册到QML中
1、简介
截止到我写这篇博客时,我所知道的注册到QML有两种方式,一种是qmlRegisterType
,另一种是通过QQmlEngine->rootContext()->setContextProperty()
注册。两种方式各有优缺点。下面分次介绍,并综合对比。
2、方式一:qmlRegisterType
2.1 注册
这种方式常见于库中,引用他人的qml库时会大量看到。比如FluentUI(注释:qml文件也可通过qmlRegisterType注册)。代码可以通过链接查看,在Fluent.cpp源文件中。
通过这种方式注册c++类型到QML中需要在qml中创建实体才能使用此类型,什么是实体?就是在一段QML中使用这个对象,同样拿FluentUI举例,FluentUI中注册了一个FluTreeView类型
qml类型注册代码
qmlRegisterType(QUrl("qrc:/com.zhuzichu/controls/FluTreeView.qml"),uri,major,minor,"FluTreeView");
c++类型注册代码
// 为什么继承QQuickItem,因为好多属性不用自己写了,比如x,y,宽高等,而且QT提供很多东西基本继承自QQuickItem
// 当然,如果不需要这些也可直接继承QObject,因为QQuickItem也是继承自QObject
class CPPRegister : public QQuickItem {
Q_OBJECT
};
qmlRegisterType<CPPRegister>("CPPRegister", 1, 0, "CPPRegister");
// main.qml
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
import CPPRegister 1.0
Window {
// 这样就会在这份QML代码中有了实体
FluTreeView {
id:tree_view
objectName: "flu_tree_view" // 名字随意,同个QML文件唯一即可
width:240
anchors{
top: parent.top
left: parent.left
bottom: parent.bottom
}
onItemClicked: {
// do something
}
Component.onCompleted: {
}
}
CPPRegister {
objectName: "cpp_register"
}
}
在之前一节说到,qml需要通过QQmlEngine及QQmlComponent创建,即一个QML文件对应于一个QQmlComponent对象。而一个QQmlEngine可以有多个QQmlComponent对象,通过同一个QQmlEngine创建出来的多个QQmlComponent不共享各自的成员。即上述main.qml中的tree_view
无法在其他qml使用,想要共享时需要使用第二种方式,在第二节会讲到。
2.2 获取QML中对象
相应地,有注册就有获取,如何在C++中使用注册的QML对象呢,qt已经提供了方法findChild
。
// 伪代码
// 之前一节通过QQmlComponent->create()会返回一个QObject类型指针,这个指针就是QML中顶层元素的
// 在上面提到的main.qml中的顶层元素使Window,其类型是QQuickWindow
CPPRegister *ptr = QQuickWindow->findChild<CPPRegister>("cpp_register");
3、方式二:setContextProperty
使用这种方式注册需要先创建C++对象,然后再将这个对象指针注册进去
CPPRegister *cppRegister = new CPPRegister();
// 名字可以随便设置, 但是需要注意不要在同一个QQmlEngine下重名
qmlEngine.rootContext()->setContextProperty("CPP_Register", cppRegister);
qml使用时需要带上注册时的名字
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
function onTrigger() {
console.log("onTrigger()")
CPP_Register.setString("Hello!")
console.log(CPP_Register.getString())
}
function onTrigger2() {
console.log("onTrigger2()")
CPP_Register.setString("World")
console.log(CPP_Register.getString())
}
Component.onCompleted: {
// 建立信号和槽的连接
CPP_Register.tiggerOnce.connect(onTrigger);
CPP_Register.tiggerOnce.connect(window.onTrigger2);
}
}
4、综合比较
方式一:
1、方便做成三方库供别人使用
2、只能在本QML使用,无法在其他QML使用
方式二:
1、注册后多个QML使用同一份数据,方便做成单例
2、 当有多个QQmlComponent都注册时容易重名。setContextProperty内部是靠哈希表加锁进行维护,后注册的会覆盖之前注册的,导致崩溃问题。
建议:如果想独自使用,建议使用方式一,想大家一起用,建议使用方式二,并注意注册时机