Qt 5.12--Qt qml 全局變量

1 簡介

全局變量在軟件配置,樣式切換,UI佈局,屬性同步等用處很大。

2 全局變量應用場景

  • 資源共享、重用
    整個應用程序相關的設置,比如說程序的版本、風格(theme)、字體資源等,這些數據適合放入一個全局變量,從而可以在整個程序的任何地方反覆使用。
  • 數據傳遞
    全局變量在代碼裏都能訪問到,相當於一塊共享的內存空間,所以可以在不同的地方傳遞數據。但如果是多線程環境下的話,需要考慮好互斥,讀寫鎖等。
  • 事件驅動
    我們可以將該變量申明爲QObject子類,然後定義好信號與槽,然後在程序需要的地方連接這些信號或者調用槽函數,這樣我們的程序就通過這個全局變量連接起來了。這就相當於有了一個核心驅動,比如只要一個信號發出來,程序中所有連接的槽函數就都會被調用,而無需關心是否漏掉了一個。

3 QML中定義全局變量

QML是需要QML引擎(即QQmlEngine)來解釋執行的,所以QML中的全局變量本質是QML執行上下文(QQmlContext)的屬性。定義QML全局變量也就是把我們的對象設置爲QML執行上下文的屬性。
單獨定義 和 批量定義 (C++形式、QML形式)

3.1 單獨定義

  • 定義一個QObject的子類,設計好它的信號、槽還有屬性;
  • 在main函數裏構建對象;
  • 在QQmlEngine構建之後還未加載任何QML文件之前,將該對象設置爲執行上下文的屬性,並取一個合理的名字:
engine->rootContext()->setContextProperty("$hub", cppBackend);

這樣$hub就成爲了QML中的全局變量,你可以直接使用它內部的各種元數據(信號、槽、屬性、枚舉類型等等)。

這裏我們約定,用$作爲全局變量的開頭字母,這個在JavaScript和QML中是合法的,便於我們區分普通局部變量和全局變量。

3.2 C++形式批量定義

如果我們的程序比較複雜,把功能都放在一個全局變量裏不合適,我們可以將它們拆開來,用不同的C++類來實現,然後定義一個總的C++類,將這些功能類作爲這個總類的屬性,主要步驟是:

1根據功能定義不同類,例如:
程序設置類:

class Settings : public QObject{
    Q_OBJECT
public:
    Q_PROPERTY(QString appName MEMBER m_appName)
private:
    QString m_appName = "MyApp";
}

和網絡類:

class Networks : public QObject{
    Q_OBJECT
}

2 定義一個總的類:

class GlobalObject : public QObject{
    Q_OBJECT
public:
    Q_PROPERTY(Settings* settings MEMBER m_settings)
    Q_PROPERTY(Networks* networks MEMBER m_networks)
private:
    Settings* m_settings;
    Networks* m_networks;
}

3 在main函數中創建總類的對象:

auto globalObject = new GlobalObject();

4 在QQmlEngine構建之後還未加載任何QML文件之前,將該對象設置爲執行上下文對象:
engine->rootContext()->setContextObject(globalObject);
QML中約定,contextObject的所有屬性都自動變爲contextProperty,就像他們用第一種方法單獨定義一樣。所以如果需要的功能比較多,建議使用批量定義的方法,更方便快捷。

需要注意,如果一個程序中同時使用了這兩種方法定義全局變量而且有變量重名了,那麼以單獨定義的優先。

3.3 QML形式批量定義

可以用QML文件來代替上面的C++總類。

在定義好Settings和Networks之後,接下去的步驟改爲:

1 將這些C++類註冊到QML中:

qmlRegisterType<Settings>("MyCppBackend", 1, 0, "Settings");
qmlRegisterType<Networks>("MyCppBackend", 1, 0, "Networks");

2 然後新建一個QML文件:

//globalobject.qml
import QtQuick 2.7
import MyCppBackend 1.0

QtObject {
    id: root
    property var $settings: Settings{}
    property var $networks: Networks{}
}

3 將該QML文件作爲QML引擎加載的第一個文件:

engine->load(QUrl("qrc:///globalobject.qml")); 

QML引擎約定,加載的第一個QML文件就是contextObject,所以和C++定義類似,它的屬性也就成了contextProperty。

和C++批量定義相比,QML批量定義有如下優勢:

變量名前面可以加$,從而方便區分全局變量和局部變量,這個在C++定義屬性的時候是不允許的;

如果某個全局變量(一般是QML對象)構造很慢,可以通過QML中的Loader來很方便異步構造,從而加速程序啓動:

property var loader: Loader{
    asynchronous: true
    source: "qrc:/UI/Main.qml"
    onLoaded: {
        // 當加載完畢會進入這裏
    }
}

QML全局變量的中樞作用
最後我們結合批量定義中的例子來看下QML中全局變量起的數據、消息中樞作用。這個主要是利用了QML的屬性綁定特性(Property Binding)。

假如說我們有兩個QML文件:

//View1
import QtQuick 2.7
Item{
    id: root
    Text{
        text: $settings.appName
    }
}

和:

//View2
import QtQuick 2.7
import QtQuick.Controls 2.2
Rectangle{
    id: root
    TextField{
        text: $settings.appName
    }
    Button{
        text: "Click!"
        onClick:{
            $settings.appName = "New Name!";
        }
    }
}

View1和View2中的text都和settingsappNameView2settings中的appName這個屬性做了綁定。當我點擊View2中的按鈕,settings.appName被修改,所有綁定的屬性也就會自動更新,不會遺忘。由於$settings是全局變量,這種用法可以深入到任意複雜、任意層級的界面中,非常方便。

參考

1、QML中使用全局變量
2、Flux

發佈了494 篇原創文章 · 獲贊 558 · 訪問量 144萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章