Qt qml创建c++类的单例对象

起因是之前用setContextProperty注册全局变量使用多了发现麻烦的点很多。

首先就是容易创建多个实例,发现不对后还得一个个打印地址检查,去找自己在哪又去new了这个对象。

只能说有适用的场景,但我之前无脑的用在需要保持单个实例的情景下确实逆天了,接下来记录一下用的新方法:

一、创建c++类的头文件和cpp文件

依旧继承QObject,然后在路径中输入想要创建的文件夹去管理.h和.cpp文件*(这样qt才会帮你去本地创建新文件夹)*

二、 示例代码

在里面写点示例代码,来验证后续是否成功将其注册为单例

c 复制代码
//testsingleton.h

#ifndef TESTSINGLETON_H
#define TESTSINGLETON_H

#include <QObject>

class testSingleton : public QObject
{
    Q_OBJECT
public:
    explicit testSingleton(QObject *parent = nullptr);

    Q_INVOKABLE  int get_Cur();

signals:


private:
    int cur=0;
};

#endif // TESTSINGLETON_H

在cpp中递增一下cur,以供检验数据一致性

c 复制代码
//testsingleton.cpp

#include "testsingleton.h"

testSingleton::testSingleton(QObject *parent)
    : QObject{parent}
{}

int testSingleton::get_Cur()
{
    return cur+=1;
}

三、注册单例

在main.cpp中引入c++类,并将其注册为单例。

  • 引入用的路径可以在CMakeLists中查看,将其复制过来即可
c 复制代码
//CMakeLists

qt_add_qml_module(apptest
    ...
    SOURCES Ctest/testsingleton.h Ctest/testsingleton.cpp
)
  • 理所当然的,要在加载engine前注册单例
c 复制代码
//main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "Ctest/testsingleton.h"

int main(int argc, char *argv[])
{
    ......
    
    qmlRegisterSingletonType<testSingleton>("single",1,0,"Single_example",[](QQmlEngine*,QJSEngine*){		//疑点
    							//依次为:(QML模块名、主版本号、次版本号、QML中的类型名、可调用对象)
        return new testSingleton();					//返回唯一实例
    });

    engine.load(url);

    return app.exec();
}

疑点:为什么参数类型得是(QQmlEngine*,QJSEngine*)呢?

  • 解:这个按住ctrl点一下,看眼源码其实就知道了,写的lambda需要匹配源码的签名(即 参数列表+返回类型
    *

    c 复制代码
    template <typename T>
    int qmlRegisterSingletonType(
    const char *uri, int versionMajor, int versionMinor, const char *typeName,
    std::function<QObject *(QQmlEngine *, QJSEngine *)> callback)

三、qml调用验证

最后在不同qml文件中调用get_Cur方法,查看结果是否会保留另个qml文件中已经递增的数据

  • 先在main.qml中,自动调用一次get_Cur
    *

    c 复制代码
    //main.qml
    
    import test 1.0
    import single 1.0
    
    Window {
        width: 640
        height: 480
        visible: true
        title: qsTr("Hello World")
    	
    	TestMd{
        	anchors.centerIn: parent
    	}
    	
    	...
    	
        Component.onCompleted: {
            console.log("first is : ",Single_example.get_Cur())
        }
    }
  • 然后在另一个qml文件中,每次按下按钮调用一次get_Cur
    *

    c 复制代码
    //TestMd.qml
    
    import QtQuick
       import QtQuick.Controls
       import single 1.0
       
       Rectangle {
           width: 200
           height: 100
           color: "blue"
       
           Button {
               anchors.centerIn: parent
               text: "button"
               width: 80
               height: 60
               onClicked: {
                   console.log("clicked is : ",Single_example.get_Cur())
               }
           }
       }

四、输出结果

进行的操作是:程序运行后,点击一次按钮

然后输出是:

过程是:

  • 初始化时cur=0
  • main.qml加载完成后自动调用一次,使得cur=1
  • 按钮的click事件调用一次,使得cur=2

由此我们也能够看出,是成功的创建完成了单例对象的。

五、另一方法(Qt 6.2+)

在询问gpt时,它给出了另一个更简便的api,直接替换qmlRegisterSingletonType,如下所示:

c 复制代码
auto *obj = new testSingleton();
qmlRegisterSingletonInstance("single", 1, 0, "Single_example", obj);
  • 区别:
qmlRegisterSingletonType qmlRegisterSingletonInstance
懒加载,创建时再回调函数 简单快捷,开始就直接创建好

六、总结

全局和单例使用的情景大不相同,之前的错误使用让我排bug时折磨了很久。 等有时间可能回去把它再统一改一次了。

在后面的项目中,得好好规划一下了。

相关推荐
身如柳絮随风扬21 分钟前
Java中的CAS机制详解
java·开发语言
-dzk-2 小时前
【代码随想录】LC 59.螺旋矩阵 II
c++·线性代数·算法·矩阵·模拟
韩立学长2 小时前
【开题答辩实录分享】以《基于Python的大学超市仓储信息管理系统的设计与实现》为例进行选题答辩实录分享
开发语言·python
froginwe112 小时前
Scala 循环
开发语言
m0_706653232 小时前
C++编译期数组操作
开发语言·c++·算法
故事和你913 小时前
sdut-Java面向对象-06 继承和多态、抽象类和接口(函数题:10-18题)
java·开发语言·算法·面向对象·基础语法·继承和多态·抽象类和接口
Bruk.Liu3 小时前
(LangChain实战2):LangChain消息(message)的使用
开发语言·langchain
qq_423233903 小时前
C++与Python混合编程实战
开发语言·c++·算法
m0_715575343 小时前
分布式任务调度系统
开发语言·c++·算法
csbysj20203 小时前
选择(Selectable)
开发语言