Qt 从C ++定义QML类型(二)
前言
在上一篇文章中介紹了如何注冊一個C++的可實例化的對象類型供 QML 中使用,那么今天繼續之前的文章介紹。
正文
注冊不可實例化類型
有時候QObject派生類可能需要在QML類型系統中注冊,但不能作為可實例化類型。例如,如果C ++類是這種情況:
1.是一個不應該實例化的接口類型
2.是不需要暴露給QML的基類類型
3.聲明了一些應該可以從QML訪問的枚舉,但除此之外不應該是可實例化的
4.通過單例實例提供給QML的類型,不應該從QML實例化
在Qt的QML模塊提供用于登記非實例類型的幾種方法:
- qmlRegisterType()(不帶參數)注冊一個C ++類型,該類型不可實例化,不能從QML引用。這使得引擎可以強制從QML實例化的任何繼承類型。
- qmlRegisterInterface()注冊具有特定QML類型名稱的Qt接口類型。該類型不是從QML實例化的,但可以通過其類型名稱引用。
- qmlRegisterUncreatableType()注冊一個不可實例化的命名C ++類型,但可以識別為QML類型系統的一個類型。如果類型的枚舉或附加屬性可以從QML訪問,但是類型本身不應該是可實例化的,那么這個方法可以用到。
- qmlRegisterSingletonType()注冊一個可以從QML導入的單例類型。
注意,使用QML類型系統注冊的所有C ++類型都必須是QObject派生的,即使是不可實例化類。
用單例類型注冊單例對象
單例類型讓屬性、信號和方法能夠暴露在名稱空間中,而不需要客戶端手動實例化對象實例。特別是QObject單例類型是提供功能或全局屬性值的一種高效方便的方法。
請注意,單例類型沒有關聯的QQmlContext,因為它們在引擎中的所有上下文之間共享。QObject單例類型實例由QQmlEngine構建并擁有,并且在引擎銷毀時將被銷毀。
一個QObject單例類型可以以類似于任何其他QObject或實例化類型的方式進行交互,除了只存在一個(引擎構造和擁有的)實例,并且它必須通過類型名稱而不是id引用。可以綁定QObject單例類型的Q_PROPERTY ,并且可以在信號處理程序表達式中使用QObject模塊API的Q_INVOKABLE函數。這使得singleton類型成為實現樣式或主題的理想方式,并且它們也可以用來代替存儲全局狀態或提供全局功能。
一旦注冊,一個QObject單例類型可以被導入和使用,就像任何其他暴露給QML的QObject實例一樣。
如下示例:
假如這里的Theme已經注冊到MyThemeModule 1.0系統空間。并且定義了 color 屬性,那么在 QML 中可以直接進行引用。
注意: QML中注冊類型的枚舉值應該以大寫字母開頭。
OK,接下來我們看看qmlRegisterSingletonType 注冊一個可以從 QML 導入的單例對象。
qmlRegisterSingletonType函數說明
qmlRegisterSingletonType一共有三個重構函數,先來看看函數的聲明。
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue(* ) ( QQmlEngine *, QJSEngine * ) callback) int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(* ) ( QQmlEngine *, QJSEngine * ) callback) int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)1.int qmlRegisterSingletonType(const char uri, int versionMajor, int versionMinor, const char *typeName, QJSValue( ) ( QQmlEngine , QJSEngine ) callback)
此函數可用于在特定的uri和typeName中注冊singleton類型提供者回調,其版本在versionMajor和versionMinor中指定。
單例類型可以是QObject或QJSValue。這個函數應該用來注冊一個單例類型的提供者函數,它返回一個QJSValue作為單例類型。
注: 如果更改,QJSValue單例類型屬性不會觸發綁定。
來看看示例:
//首先,定義單例類型提供者回調函數 static QJSValue example_qjsvalue_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) {Q_UNUSED(engine)static int seedValue = 5;QJSValue example = scriptEngine->newObject();example.setProperty("someProperty", seedValue++);qDebug() << __FUNCTION__ << seedValue;return example; }int main(int argc, char *argv[]) {QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);QQmlApplicationEngine engine;//第二,通過在初始化函數中調用該函數,用QML注冊單例類型提供程序qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider);engine.load(QUrl(QLatin1String("qrc:/main.qml")));if (engine.rootObjects().isEmpty())return -1;return app.exec(); }這里在注冊的時候也可以寫成 Lambda 函數:如下:
qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue {Q_UNUSED(engine)static int seedValue = 5;QJSValue example = scriptEngine->newObject();example.setProperty("someProperty", seedValue++);return example; });在 QML 中調用:
import QtQuick 2.0 import Qt.example.qjsvalueApi 1.0 as ExampleApi Item {id: rootproperty int someValue: ExampleApi.MyApi.somePropertyComponent.onCompleted: {console.log("someValue = ",someValue)} }程序輸出:
example_qjsvalue_singletontype_provider 6 qml: someValue = 5注意,這里的someValue再次調用還是保持之前的值,也就是說不會重復進入到回調函數中。經過多次測試均是如此。
個人覺得上面這種用法在實際的場景中應該使用得不多,那么接下來看看第二個。
2.int qmlRegisterSingletonType(const char uri, int versionMajor, int versionMinor, const char *typeName, QObject (* ) ( QQmlEngine , QJSEngine ) callback)
和上面那個注冊函數不同的是,這里是返回一個 QObject 對象類型的回調。
一個QObject單例類型可以通過它所注冊的類型名稱被引用,并且這個類型名稱可以用作Connections類型中的目標,或者用作任何其他類型的id。一個例外是QObject單例類型屬性不能被別名(因為單例類型名稱不能識別與其他任何項目相同的組件中的對象)。
注:一個QObject的從單類型提供者返回單類型實例是由QML引擎所有,除非對象有明確的QQmlEngine :: CppOwnership標志設置。
來看看示例:
//定義一個繼承于 QObject 的功能類class Student : public QObject {Q_OBJECTQ_PROPERTY(QString name READ getName WRITE setName NOTIFY sigNameChanged)public:explicit Student(QObject *parent = nullptr):QObject(parent){}~Student(){}void setName(const QString & name){if(name != m_name){m_name = name;emit sigNameChanged(m_name);}}QString getName() const {return m_name;}Q_INVOKABLE QString doSomething(){qDebug(__FUNCTION__);setName("xiaoming");return m_name;}signals:void sigNameChanged(QString name);private:QString m_name = "zhangsan";};//第二,定義單例類型提供者函數(回調) static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) {Q_UNUSED(engine)Q_UNUSED(scriptEngine)qDebug()<<__FUNCTION__;Student *student = new Student();return student; }調用函數注冊:
qmlRegisterSingletonType<Student>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider);在 QML 中使用:
import QtQuick 2.0 import Qt.example.qobjectSingleton 1.0Item {id: rootproperty string someValue: MyApi.nameComponent.onCompleted: {console.log("someValue = ",someValue)someValue = MyApi.doSomething()console.log("someValue = ",someValue)} }運行代碼,輸出如下:
example_qobject_singletontype_provider qml: someValue = zhangsan doSomething qml: someValue = xiaoming注意,由于singleton類型沒有關聯的QQmlContext對象,因此在注冊為單例類型實現的QObject衍生類型的函數內,QML上下文和引擎信息不可用。
3.int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
該函數可用于在從uri導入的庫中注冊名為qmlName的單例類型,其版本號由versionMajor和versionMinor組成。該類型由位于url的QML文件定義。該網址必須是絕對網址。
另外,該類型的QML文件在其導入語句中必須包含編譯指令Singleton語句。
單例類型可以通過它所注冊的類型名稱來引用,并且此類型名稱可以用作Connections類型中的目標,或者用作任何其他類型的id。單例類型屬性不能被別名。
看看示例:
為了在QML中使用已注冊的單例類型,必須導入單例類型。
import QtQuick 2.0 import Qt.example.qobjectSingleton 1.0 Item {id: rootproperty int someValue: RegisteredSingleton.testProp1 }關于單例類型導入先介紹到這里。
總結
以上是生活随笔為你收集整理的Qt 从C ++定义QML类型(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Qt 从C ++定义QML类型(一)
- 下一篇: Qt 设置应用程序图标