30.qt quick-使用qmlRegisterSingletonType註冊單例類給QML使用
阿新 • • 發佈:2021-06-19
上章,我們學習使用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 { width: 300 height: 90 } CfgRectangle { width: 300 height: 90 } CfgRectangle { width: 300 height: 90 } }
未完待續~