您现在的位置是:首页 >技术交流 >c++类型注册到QML中网站首页技术交流

c++类型注册到QML中

山有木兮啊 2024-06-17 11:19:27
简介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 CPPRegisterpublic 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");

详细的代码请去github

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);
    }
}

详细的代码请去github

4、综合比较

方式一:
1、方便做成三方库供别人使用
2、只能在本QML使用,无法在其他QML使用
方式二:
1、注册后多个QML使用同一份数据,方便做成单例
2、 当有多个QQmlComponent都注册时容易重名。setContextProperty内部是靠哈希表加锁进行维护,后注册的会覆盖之前注册的,导致崩溃问题。

建议:如果想独自使用,建议使用方式一,想大家一起用,建议使用方式二,并注意注册时机

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。