zoukankan      html  css  js  c++  java
  • 30.qt quick-使用qmlRegisterSingletonType注册单例类给QML使用

    上章,我们学习使用qmlRegisterType()注册C++类到QML中.本章我们来学习qmlRegisterSingletonType,如何注册单例类给QML使用。

    1.qmlRegisterSingletonType函数介绍

    qmlRegisterSingletonType函数模版声明如下:

    template<typename T>
    int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(*)(QQmlEngine *, QJSEngine *) callback);
    
    template<typename T>
    int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName);
    • 第一个带callback参数的是用来将C++类实例化后,然后保存到QML全局环境中,供QML使用
    • 第二个是用来将QML类型实例化为单例类给QML使用(这个更简单了,直接参考帮助文档即可,但是单例类无法创建可视化的QML类,因为未指定父类就已经生成了)

    单例类在应用开发很常见,比如配置文件类、系统信息类等,因为可能在每个页面都可能去访问它.

    本章我们来实现一个 Xml配置文件的C++类,然后通过qmlRegisterSingletonType来单例化给QML使用.

    2.demo实现效果

     如上图所示,我们这里创建了3个CfgRectangle控件,每个控件都是获取的XmlCfg单例类,当我们更改第1个CfgRectangle控件的值时,其它控件也跟着改变了.

    最后当我们关闭应用程序后,便将更改后的配置信息写回cfg.xml配置文件中.

    3.xmlcfg.h:

    class XmlCfg : public QObject
    {
        Q_OBJECT
    
    public:
        enum XmlName{
            Start  =0,
            Addr   ,
            CurrentUser,
            End
        };
        Q_ENUM(XmlName)
    
        explicit XmlCfg(QObject *parent = nullptr);
        ~XmlCfg();
    
        Q_INVOKABLE QString read(XmlName name);           //读出数据
        Q_INVOKABLE bool write(XmlName name,QString value);  //向name写入value数据
        Q_INVOKABLE bool writeXmlFile();                // 更新配置文件(程序退出时调用一次)
    
    private:
        class XmlAttribute{     // 保存配置数据
        public:
               XmlName  name;
               QString  value;
    
               XmlAttribute(XmlName name, QString value)
               {
                   this->name = name;
                   this->value = value;
               }
        };
    
    
    private:
        static QString m_xmlFileName;
        QList<XmlAttribute> attrList;          // 记录属性
        QMetaEnum m_metaState;
        bool m_fileIsUpdate;                   // 文件是否刷新
    
        bool readXmlFile();                    // 读取配置文件
    
        void initXmlList();
    
    
    signals:
        void writeUpdate(XmlName name);
    
    };
    
    
    // 定义一个回调类指针,用于接收回调.
    static QObject *xmlCfg_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
    {
        //Q_UNUSED: 向编译器指示参数未在函数的主体中使用。这可用于抑制编译器警告
        Q_UNUSED(engine)
        Q_UNUSED(scriptEngine)
    
        XmlCfg *cfg = new XmlCfg();
        return cfg;
    }

    4.xmlcfg.cpp:

    QString XmlCfg::m_xmlFileName = "cfg.xml";
    
    XmlCfg::XmlCfg(QObject *parent) : QObject(parent),
        m_metaState(QMetaEnum::fromType<XmlCfg::XmlName>())
    {
    
        initXmlList();
        readXmlFile();
    }
    
    XmlCfg::~XmlCfg()
    {
        if (m_fileIsUpdate) {
            qDebug()<<"~XmlCfg : writeXmlFile";
            writeXmlFile();
        }
    
    }
    
    
    void XmlCfg::initXmlList()         // 初始化
    {
        attrList.clear();
        attrList<<XmlAttribute(XmlName::Addr, "127.0.0.1");
        attrList<<XmlAttribute(XmlName::CurrentUser, "诺谦");
    }
    
    QString XmlCfg::read(XmlName name)     //读出数据
    {
        QString ret("");
    
        foreach (XmlAttribute attr, attrList)
        if(attr.name == name) {
            ret = attr.value;
        }
        return ret;
    }
    
    bool XmlCfg::write(XmlName name,QString value)  //向name写入value数据
    {
        bool ret(false);
    
        for(int i =0; i<attrList.count(); i++)
        if(attrList[i].name == name)
        {
            qDebug()<<"write:"<<m_metaState.valueToKey(name)<<value;
            if (attrList[i].value != value) {
                attrList[i].value = value;
                ret =true;
                m_fileIsUpdate = true;
                emit writeUpdate(name);
            }
            break;
        }
    
        return ret;
    }
    
    bool XmlCfg::readXmlFile()      // 读取配置文件
    {
        QFile  file(QApplication::applicationDirPath() +"//"+ m_xmlFileName);
    
    
        // 打开失败 那就是都是默认值
        if(!file.open(QFile::ReadOnly | QFile::Text)) {
            file.close();
            writeXmlFile();
            return false;
        }
    
        QXmlStreamReader* reader=new QXmlStreamReader(&file);
    
         while(!reader->atEnd()) {
    
            QString name = reader->name().toString();
    
            if(reader->isStartElement()) {
                if(name=="index") {
                    reader->readNext();
                } else { //读取其它值
                    bool isOk = false;
                    XmlName xmlName = (XmlName)m_metaState.keyToValue(name.toLocal8Bit(), &isOk); // 字符串转枚举
                    if (!isOk) {
                        qDebug()<<"readXmlFile !isOk "<<name;
                    }
                    write(xmlName, reader->readElementText());
                }
            }
    
            if(reader->isEndElement()) {
    
            }
            reader->readNext();
        }
    
        file.close();
        m_fileIsUpdate = false;
        if(reader->hasError())
        {
           qDebug()<<"readXml ERR:"<<reader->errorString();
           initXmlList();        //初始化设置
           writeXmlFile();
           return false;
        }
        return true;
    }
    
    bool XmlCfg::writeXmlFile()     // 更新配置文件(程序退出时调用一次)
    {
        QFile  file(QApplication::applicationDirPath() +"//"+ m_xmlFileName);
    
        QFileInfo info(file);
    
        if(!file.open(QFile::WriteOnly | QFile::Text))
        {
            qDebug()<<"file write error";
            return false;
        }
    
        QXmlStreamWriter* writer=new QXmlStreamWriter(&file);
        QXmlStreamAttributes attributes;
    
        writer->setCodec("utf-8");
        writer->setAutoFormatting(true);
        writer->writeStartDocument();
    
        writer->writeStartElement("index");
    
        writer->writeComment("配置");
        //写入所有存值
        for(int i = (int)XmlName::Start + 1; i<(int)XmlName::End; i++)
        {
             QString name = m_metaState.valueToKey(i);   // 枚举转字符串
             QString value = read((XmlName)i);
             writer->writeTextElement(name,value);
        }
    
    
        writer->writeComment("配置完成");
        writer->writeEndElement();
        writer->writeEndDocument();
    
        file.close();
        return true;
    }

    每次创建XmlCfg时,会从cfg.xml中读取出当前配置信息.并保存到attrList成员中.
    当程序退出的时候,则会调用析构函数,如果m_fileIsUpdate为true,则更新内容到cfg.xml中

    然后我们在main.cpp中注册单例类:

    qmlRegisterSingletonType<XmlCfg>("Qt.Singleton", 1, 0, "XmlCfg", xmlCfg_qobject_singletontype_provider);

    5.实现CfgRectangle.qml
    然后我们实现一个CfgRectangle.qml文件,用来读写配置信息的控件:

    import QtQuick 2.0
    import QtQuick.Layouts 1.12
    import QtQuick.Controls 2.12
    import Qt.Singleton 1.0
    Rectangle {
       ... ...
    function writeAddr(addr) { XmlCfg.write(XmlCfg.Addr, addr); } function writeUser(user) { XmlCfg.write(XmlCfg.CurrentUser, user); } GridLayout { ... ... TextArea { id: areaAddr implicitWidth: 200 text : XmlCfg.read(XmlCfg.Addr) // 读取XML文件中的 Addr元素的内容 color: "#0E0E0E" font.pixelSize: 14 font.family: "Microsoft Yahei" background: Rectangle { anchors.fill: parent border.color: "#E0E0E0" color: "#FFFFFF" radius: 4 } Keys.onPressed: { if ((event.key == Qt.Key_Return || event.key == Qt.Key_Enter ) ) { writeAddr(text) event.accepted = true } } }      ... ... TextArea { id: areaCurrentUser implicitWidth: 200 text : XmlCfg.read(XmlCfg.CurrentUser) // 读取XML文件中的 CurrentUser元素的内容 color: "#0E0E0E" font.pixelSize: 14 font.family: "Microsoft Yahei" background: Rectangle { anchors.fill: parent border.color: "#E0E0E0" color: "#FFFFFF" radius: 4 } Keys.onPressed: { if ((event.key == Qt.Key_Return || event.key == Qt.Key_Enter ) ) { writeUser(text) event.accepted = true } } } } Connections { target: XmlCfg; onWriteUpdate:{ // 重新读取XML文件中元素的内容 switch (name) { case XmlCfg.Addr: areaAddr.text = XmlCfg.read(XmlCfg.Addr); case XmlCfg.CurrentUser: areaCurrentUser.text = XmlCfg.read(XmlCfg.CurrentUser); } } } }

    最后我们在main.qml中,创建多个CfgRectangle控件

    Column {
            anchors.centerIn: parent
            spacing: 3
            Text {
                text: " 输入回车即可保存到配置文件中:";
                font.pixelSize: 20
                color: "#444"
                font.family: "Microsoft Yahei"
            }
            CfgRectangle {
                 300
                height: 90
            }
            CfgRectangle {
                 300
                height: 90
            }
            CfgRectangle {
                 300
                height: 90
            }
    }

    未完待续~



    人间有真情,人间有真爱。

    如果您喜欢这里,感觉对你有帮助,并且有多余的软妹币的话,不妨投个食吧,赞赏的时候,留下美句和你的博客地址哦~   戳这里看谁投食了


查看全文
  • 相关阅读:
    vue hover如何触发事件?
    防止 window.open 被拦截
    input输入框change和blur事件区别
    神马?使用JS直接上传并预览粘贴板的图片?
    删除设备与驱动器中百度网盘图标
    枚举类字典代码 草稿
    中文转换成阿拉伯数字
    Java对象与类中的一个小练习
    正向代理和反向代理
    MySQL教程126-MySQL事务隔离级别
  • 原文地址:https://www.cnblogs.com/lifexy/p/14904760.html
  • Copyright © 2011-2022 走看看