QML與C++混合編程[官方文檔彙編]

概述:

QML架構實現的目的就是把UI設計與後臺程序邏輯分離,實現快速的前端UI設計,提供簡潔而功能豐富的動畫效果。QML表象上更像Web開發中的前端腳手架,通過集成JavaScript的開發優勢實現前端可視元素的編程控制。

QT整個架構的靈魂就是強大的元對象系統( meta object system),通過元對象系統提供的信號機制實現QT開發的解決方案。QML作爲QT解決方案的重要組成模塊,必然繼承強大的元對象系統( meta object system)。我們通過元對象系統( meta object system)可以通過C++代碼輕鬆集成、擴展QML。這樣我們就能輕鬆的把前端UI(QML)與後端C++(後端程序邏輯)分離。

QML模塊中的類允許我們從C++加載和處理QML對象,並且QML引擎與QT的元對象系統完美的集成。使得C++代碼可以直接從QML調用,也就是我們在QML裏可以輕易的調用C++代碼功能。QML的這種能力使得我們能夠開發混合應用程序,該混合應用程序將QML、JavaScript和C++代碼混合在一起實現。我們甚至可以使用Web的開發思維,使用JSON數據格式鏈接前端與後端進行開發。更確切的稱之爲MVC開發思維。

 

QML和C++的混合開發:

QT給我們提供了以下幾種方式實現QML和C++的混合開發:

  • 通過QML和JavaScript實現前端UI,使用C++實現後臺邏輯,將用戶界面代碼與應用程序邏輯代碼分開。
  • 通過QML引擎給出的接口環境,訪問C++中的功能。例如:調用應用程序邏輯,使用從C++實現的數據模型或調用的第三方C++庫中的某些方法。這種方式,可以讓我們訪問在C++中暴露的各種屬性與方法。
  • 訪問Qt QML 或Qt Quick C++ API中的功能。這中方式只是調用引擎自帶的C++功能。
  • 從C++實現QML對象類型,在QML中像使用其他QML組件類型一樣使用。以完整對象組件的方式。

要向QML提供一些C++數據或功能,必須從QObjt派生類中獲得。由於QML引擎與元對象系統的集成,任何QObject派生類的屬性、方法和信號都可以從QML訪問,如將C++類型的屬性暴露爲QML所描述的那樣。一旦此類提供了所需的功能,就可以通過多種方式將其公開給QML:

  • 類可以註冊爲可實例化的QML類型,這樣它就可以像QML代碼中的任何普通QML對象類型一樣被實例化和使用。[C++類註冊爲組件服務於QML:這將完整的暴露給QML,以組件的形式。]

  • 類可以註冊爲單例類型,這樣類的單個實例可以從QML代碼導入,從而允許從QML訪問實例的屬性、方法和信號。[C++類註冊爲組件服務於QML:這將完整的暴露給QML,以組件的形式。單例模式註冊,C++類也必定是單例模式類,否則這種註冊將無效。]

  • 類的實例可以作爲上下文屬性或上下文對象嵌入到QML代碼中,從而允許從QML訪問實例的屬性、方法和信號。[C++類裏的方法和屬性嵌入到QML上下文中,C++類的實例化將在QML引擎初始化之前初始化。C++類不會完全暴露給QML。只有特定聲明的方法和屬性。]

這些是從QML代碼訪問C++功能的最常用方法;對於更多的選項和細節,請參見下面的文檔翻譯。此外,除了從QML訪問C++功能之外,QT QML模塊還提供了反向操作的方法,並從C++代碼中操縱QML對象。

C++代碼可以集成到C++應用程序或C++插件中,這取決於它是否被作爲獨立的應用程序或庫來分發。插件可以與QML模塊集成,然後可以在其他應用程序中導入和使用QML代碼;

 

C++與QML之間正確的集成方法選擇:

要快速確定哪種集成方法適合您的情況,可以使用以下流程圖:

 

 


將C ++類的屬性公開給QML:


由於QML引擎與QT元對象系統的集成,QML可以很容易地從C++擴展。此集成允許從QML訪問任何QObject派生類的屬性、方法和信號:可以讀取和修改屬性,可以從JavaScript表達式調用方法,並根據需要自動爲信號創建信號處理程序。另外,QObject派生類的枚舉值可以從QML訪問。

QML可以用C++代碼中定義的功能輕鬆擴展。由於QML引擎與Qt元對象系統緊密集成,QObject派生類適當公開的任何功能都可以從QML代碼訪問。這使得C++數據和函數可以直接從QML訪問,通常很少或沒有修改。

QML引擎可以通過元對象系統對QObject實例進行自省。 這意味着任何QML代碼都可以訪問QObject派生類的實例的以下成員:

  • 屬性
  • 方法(假設它們是公共插槽或用Q_INVOKABLE標記的方法)
  • 信號

此外,如果枚舉已經用Q_ENUMS聲明,則可以使用。有關更多詳細信息,請參見QML和C ++之間的數據類型轉換。

通常,無論QObject派生類是否已註冊到QML類型系統,都可以從QML訪問這些類。但是,如果要以:需要引擎訪問其他類型信息的方式使用類(例如,如果要將類本身用作方法參數或屬性,或者要以這種方式使用其枚舉類型之一),則可能需要註冊該類。

還要注意,本文檔介紹的許多重要概念在“用C ++編寫QML擴展”教程中得到了演示與證明。

數據類型處理和所有權:

任何從C++傳遞到QML的數據,無論是作爲屬性值、方法參數還是返回值,還是信號參數值,都必須是QML引擎支持的類型。

默認情況下,引擎支持許多QT C++類型,並且可以從QML中自動轉換它們。另外,用QML類型系統註冊的C++類可以用作數據類型,如果它們的枚舉可以適當地註冊,它們也可以。請參閱QML和C++之間的數據類型轉換,以獲取更多信息。

此外,數據所有權規則考慮到當數據從C++傳遞到QML時。當數據從C ++傳輸到QML時,數據所有權始終由C ++保留。 該規則的例外情況是從顯式C ++方法調用返回QObject時:在這種情況下,除非通過調用QQmlEngine將對象的所有權顯式設置爲與C ++一起使用,否則QML引擎假定該對象的所有權: 使用QQmlEngine :: CppOwnership指定的setObjectOwnership()。

此外,QML引擎尊重Qt C ++對象的常規QObject父所有權語義,並且永遠不會刪除具有父對象的QObject實例。

公開屬性:

可以使用Q_property()宏爲任何QObject派生類指定屬性。屬性是具有關聯的讀取函數和可選的寫入函數的類數據成員。

例如,下面是帶有author屬性的Message類。 如Q_PROPERTY宏調用所指定,此屬性可通過author()方法讀取,並且可通過setAuthor()方法寫入:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
    void setAuthor(const QString &a) {
        if (a != m_author) {
            m_author = a;
            emit authorChanged();
        }
    }
    QString author() const {
        return m_author;
    }
signals:
    void authorChanged();
private:
    QString m_author;
};

如果從C ++加載名爲MyItem.qml的文件時將此類的實例設置爲上下文屬性,則:

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view;
    Message msg;
    view.engine()->rootContext()->setContextProperty("msg", &msg);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}

然後,可以從MyItem.qml讀取author屬性:

// MyItem.qml
import QtQuick 2.0

Text {
    width: 100; height: 100
    text: msg.author    // invokes Message::author() to get this value

    Component.onCompleted: {
        msg.author = "Jonah"  // invokes Message::setAuthor()
    }
}

爲了最大程度地實現與QML的互操作性,任何可寫屬性都應具有一個關聯的NOTIFY信號,只要該屬性值發生更改,該信號便會發出。 這允許將屬性與屬性綁定一起使用,這是QML的基本功能,它通過在屬性的任何依賴關係值發生變化時自動更新屬性來強制屬性之間的關係。

在上面的示例中,如Q_PROPERTY()宏調用中所指定,author屬性的關聯NOTIFY信號爲authorChanged。 這意味着每當發出信號時(如作者在Message :: setAuthor()中進行更改時一樣),這會通知QML引擎必須更新所有涉及author屬性的綁定,並且引擎將更新文本。 通過再次調用Message :: author()設置屬性。

如果author屬性是可寫的,但沒有關聯的NOTIFY信號,則將使用Message :: author()返回的初始值來初始化文本值,但以後對該屬性進行的任何更改均不會更新該文本值。 另外,從QML綁定到該屬性的任何嘗試都將產生來自引擎的運行時警告。

注意:建議將NOTIFY信號命名爲<property> Changed,其中<property>是屬性的名稱。 由QML引擎生成的關聯的屬性更改信號處理程序將始終採用on <Property> Changed的形式,而不管相關C ++信號的名稱如何,因此建議信號名稱遵循此約定以避免任何混淆。

使用通知信號(NOTIFY)的注意事項:

爲了防止循環或過度計算,開發人員應該確保只有在屬性值實際更改時纔會發出屬性更改信號。此外,如果一個屬性或一組屬性很少使用,則允許對多個屬性使用相同的通知信號。這樣做時應小心,以確保性能不會受到影響。

通知信號(NOTIFY)的存在確實會產生少量開銷。在某些情況下,屬性的值在對象構造時設置,並且隨後不會更改。最常見的情況是,當類型使用分組屬性時,分組屬性對象只分配一次,並且只在刪除對象時釋放。在這些情況下,常量屬性(CONSTANT)可以添加到屬性聲明,而不是通知信號(NOTIFY)

CONSTANT屬性只能用於其值在類構造函數中已設置並最終確定的屬性。 想要在綁定中使用的所有其他屬性應改爲具有NOTIFY信號。

 

對象類型的屬性:

如果對象類型已在QML類型系統中正確註冊,則可以從QML訪問對象類型屬性。

例如,消息類型可能具有MessageBody*類型的body屬性:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged)
public:
    MessageBody* body() const;
    void setBody(MessageBody* body);
};

class MessageBody : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged)
// ...
}

假設Message類型已在QML類型系統中註冊,從而可以將其用作QML代碼中的對象類型:

Message {
    // ...
}

如果MessageBody類型也已在類型系統中註冊,則可以從QML代碼中將MessageBody分配給Message的body屬性:

Message {
    body: MessageBody {
        text: "Hello, world!"
    }
}

 

對象列表類型的屬性:

包含QObject派生類型列表的屬性也可以公開給QML。但是,出於這個目的,應該使用qqmlistproperty而不是QList<T>作爲屬性類型。這是因爲QList不是QObject派生的類型,因此無法通過Qt元對象系統提供必要的QML屬性特性,例如修改列表時的信號通知。

例如,下面的MessageBoard類具有一個QQmlListProperty類型的messages屬性,該屬性存儲Message實例的列表:

class MessageBoard : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
public:
    QQmlListProperty<Message> messages();

private:
    static void append_message(QQmlListProperty<Message> *list, Message *msg);

    QList<Message *> m_messages;
};

MessageBoard :: messages()函數僅從其QList <T> m_messages成員創建並返回QQmlListProperty,並根據QQmlListProperty構造函數的要求傳遞適當的列表修改函數:

QQmlListProperty<Message> MessageBoard::messages()
{
    return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message);
}

void MessageBoard::append_message(QQmlListProperty<Message> *list, Message *msg)
{
    MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object);
    if (msg)
        msgBoard->m_messages.append(msg);
}

注意,QQmlListProperty的模板類類型(在本例中是消息)必須註冊到QML類型系統。

 

分組屬性:

任何只讀對象類型屬性都可以作爲分組屬性從QML代碼中訪問。這可用於公開一組相關屬性,這些屬性描述類型的一組屬性。

例如,假設Message :: author屬性的類型爲MessageAuthor,而不是簡單的字符串,其子屬性爲name和email:

class MessageAuthor : public QObject
{
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QString email READ email WRITE setEmail)
public:
    ...
};

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageAuthor* author READ author)
public:
    Message(QObject *parent)
        : QObject(parent), m_author(new MessageAuthor(this))
    {
    }
    MessageAuthor *author() const {
        return m_author;
    }
private:
    MessageAuthor *m_author;
};

可以使用QML中的分組屬性語法來編寫author屬性,如下所示:

Message {
    author.name: "Alexandra"
    author.email: "[email protected]"
}

作爲分組屬性公開的類型不同於對象類型屬性,因爲分組屬性是隻讀的,並且在構造時由父對象初始化爲有效值。可以從QML修改分組屬性的子屬性,但分組屬性對象本身不會更改,而對象類型屬性可以隨時從QML分配新的對象值。因此,分組屬性對象的生命週期受到C++父實現的嚴格控制,而對象類型屬性可以通過QML代碼自由創建和銷燬。

 

公開方法(包括Qt插槽):

QObject派生類型的任何方法都可以從QML代碼訪問,如果它是:

  • 用Q_INVOKABLE()宏標記的公共方法

  • 作爲公共Qt插槽的方法

例如,下面的MessageBoard類具有已用Q_INVOKABLE宏標記的postMessage()方法以及作爲公共插槽的refresh()方法:

class MessageBoard : public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE bool postMessage(const QString &msg) {
        qDebug() << "Called the C++ method with" << msg;
        return true;
    }

public slots:
    void refresh() {
        qDebug() << "Called the C++ slot";
    }
};

如果將MessageBoard的實例設置爲文件MyItem.qml的上下文數據,則MyItem.qml可以調用以下方法中所示的兩個方法:

//C++代碼
int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    MessageBoard msgBoard;
    QQuickView view;
    view.engine()->rootContext()->setContextProperty("msgBoard", &msgBoard);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}

//QML代碼
// MyItem.qml
import QtQuick 2.0

Item {
    width: 100; height: 100

    MouseArea {
        anchors.fill: parent
        onClicked: {
            var result = msgBoard.postMessage("Hello from QML")
            console.log("Result of postMessage():", result)
            msgBoard.refresh();
        }
    }
}

如果C ++方法具有QObject *類型的參數,則可以使用對象ID或引用該對象的JavaScript var值從QML傳遞參數值。

QML支持重載的C ++函數的調用。 如果有多個具有相同名稱但參數不同的C ++函數,則將根據提供的參數的數量和類型來調用正確的函數。

當從QML中的JavaScript表達式訪問時,從C++方法返回的值將轉換爲JavaScript值。

 

公開信號(暴露信號:Signals):

可以從QML代碼訪問任何QObject派生類型的公共信號。

QML引擎會自動爲QML使用的任何QObject派生類型的信號創建信號處理程序。 信號處理程序始終在<Signal>上命名,其中<Signal>是信號的名稱,首字母大寫。 信號傳遞的所有參數均可通過參數名稱在信號處理程序中使用。

例如,假設MessageBoard類具有帶有單個參數subject的newMessagePosted()信號:

class MessageBoard : public QObject
{
    Q_OBJECT
public:
   // ...
signals:
   void newMessagePosted(const QString &subject);
};

如果MessageBoard類型已在QML類型系統中註冊,則在QML中聲明的MessageBoard對象可以使用名爲onNewMessagePosted的信號處理程序接收newMessagePosted()信號,並檢查主題參數值:

MessageBoard {
    onNewMessagePosted: console.log("New message received:", subject)
}

與屬性值和方法參數一樣,信號參數必須具有QML引擎支持的類型。 請參見QML和C ++之間的數據類型轉換。 (使用未註冊的類型不會產生錯誤,但是無法從處理程序中訪問參數值。)

類可能包含多個具有相同名稱的信號,但是隻有最終信號才能作爲QML信號進行訪問。 請注意,名稱相同但參數不同的信號無法相互區分。

 


從C++中定義QML類型


可以在C ++中定義QML類型,然後在QML類型系統中註冊。 這允許將C ++類實例化爲QML對象類型,從而使自定義對象類型可以用C ++實現並集成到現有QML代碼中。 C ++類也可能出於其他目的而註冊:例如,可以將其註冊爲Singleton Type,以使單個類實例可以通過QML代碼導入,或者可以註冊,以啓用非實例化的枚舉值 類可從QML訪問。

此外,Qt QML模塊提供了定義與諸如附加屬性和默認屬性之類的QML概念集成的QML類型的機制。

使用C ++代碼擴展QML時,可以在QML類型系統中註冊C ++類,以使該類可用作QML代碼中的數據類型。 儘管可以從QML訪問任何QObject派生類的屬性,方法和信號,如將C ++類型的屬性暴露給QML中所討論的那樣,但直到將其註冊到類型系統後,此類才能用作QML的數據類型。 另外,註冊還可以提供其他功能,例如允許從QML將類用作可實例化的QML對象類型,或者允許從QML導入和使用該類的單例實例。

此外,Qt QML模塊提供了用於實現QML特定功能的機制,例如C ++中的附加屬性和默認屬性。

(請注意,使用C ++編寫QML擴展教程演示了本文檔中涵蓋的許多重要概念。)

向QML類型系統註冊C ++類型:

可以將QObject派生的類註冊到QML類型系統中,以使該類型可用作QML代碼中的數據類型。

該引擎允許實例化和非實例化類型的註冊。 註冊可實例化的類型可使C ++類用作QML對象類型的定義,從而允許將其用於QML代碼的對象聲明中以創建此類型的對象。 註冊還爲引擎提供了其他類型的元數據,使該類型(以及該類聲明的任何枚舉)可用作屬性值,方法參數和返回值以及在QML和C ++之間交換的信號參數的數據類型。

註冊非實例化類型也以這種方式將類註冊爲數據類型,但是該類型不能用作從QML實例化爲QML對象類型。 例如,如果某個類型具有應暴露給QML的枚舉但該類型本身不應實例化,則此功能很有用。

有關選擇正確方法將C ++類型公開給QML的快速指南,請參閱在C ++和QML之間選擇正確的集成方法。參考上面的導圖。

註冊實例化對象類型:

任何QObject派生的C ++類都可以註冊爲QML對象類型的定義。 一旦在QML類型系統中註冊了一個類,就可以像聲明QML代碼中的任何其他對象類型一樣聲明和實例化該類。 一旦創建,就可以從QML中操縱類實例。 正如將C ++類型的屬性暴露給QML所解釋的那樣,可以從QML代碼訪問任何QObject派生類的屬性,方法和信號。

要將QObject派生的類註冊爲可實例化的QML對象類型,請調用qmlRegisterType()將該類作爲QML類型註冊到特定的類型名稱空間中。 然後,客戶端可以導入該名稱空間以使用該類型。

例如,假設有一個具有author和creationDate屬性的Message類:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
    Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
    // ...
};

可以通過使用適當的類型名稱空間和版本號調用qmlRegisterType()來註冊此類型。 例如,要使該類型在版本1.0的com.mycompany.messaging命名空間中可用:

qmlRegisterType<Message>("com.mycompany.messaging", 1, 0, "Message");

可以在QML的對象聲明中使用該類型,並且可以按照以下示例讀取和寫入其屬性:

import com.mycompany.messaging 1.0

Message {
    author: "Amelie"
    creationDate: new Date()
}

註冊非實例類型:

有時,可能需要向QML類型系統註冊QObject派生的類,而不是將其註冊爲可實例化的類型。 例如,如果是C ++類,就是這種情況:

  • 是不應可實例化的接口類型
  • 是不需要公開給QML的基類類型
  • 聲明一些枚舉,這些枚舉應可從QML訪問,但其他枚舉不應可實例化
  • 是應通過單實例提供給QML的類型,不應從QML實例化

Qt QML模塊提供了幾種註冊非實例類型的方法:

  • qmlRegisterType()(不帶參數)註冊了不可實例化且無法從QML引用的C ++類型。 這使引擎可以強制從QML實例化任何繼承的類型。

  • qmlRegisterInterface()註冊現有的Qt接口類型。 該類型不能從QML實例化,並且不能使用它聲明QML屬性。 但是,從QML使用這種類型的C ++屬性將執行預期的接口強制轉換。

  • qmlRegisterUncreatableType()註冊一個命名的C ++類型,該類型不可實例化,但應標識爲QML類型系統的類型。 如果應從QML訪問類型的枚舉或附加屬性,但該類型本身不可實例化,則此方法很有用。

  • qmlRegisterSingletonType()註冊一個可以從QML導入的單例類型,如下所述。

請注意,向QML類型系統註冊的所有C ++類型都必須是QObject派生的,即使它們不是不可實例化的。

用單例類型註冊單例對象:

單例類型使屬性,信號和方法可以在命名空間中公開,而無需客戶端手動實例化對象實例。 特別是QObject單例類型是一種提供功能或全局屬性值的高效便捷的方法。

請注意,單例類型沒有關聯的QQmlContext,因爲它們在引擎中的所有上下文之間共享。 QObject單例類型實例由QQmlEngine構造和擁有,並且在銷燬引擎時將銷燬。

可以以與任何其他QObject或實例化類型類似的方式與QObject單例類型進行交互,除了將僅存在一個(引擎構造並擁有)實例,並且必須通過類型名稱而不是id進行引用。 可以綁定QObject單例類型的Q_PROPERTY,並且可以在信號處理程序表達式中使用QObject模塊API的Q_INVOKABLE函數。 這使單例類型成爲實現樣式或主題的理想方式,並且它們也可以代替導入“ ..pragma library”腳本來使用,以存儲全局狀態或提供全局功能。

一旦註冊,就可以像導入QML的任何其他QObject實例一樣導入和使用QObject單例類型。 下面的示例假定QObject單例類型已註冊到版本爲1.0的“ MyThemeModule”命名空間中,其中QObject具有QColor“ color” Q_PROPERTY:

import MyThemeModule 1.0 as Theme

Rectangle {
    color: Theme.color // binding.
}

QJSValue也可以作爲單例類型公開,但是客戶應注意,此類單例類型的屬性無法綁定。

有關如何實現和註冊新的單例類型以及如何使用現有的單例類型的更多信息,請參見qmlRegisterSingletonType()

注意:QML中已註冊類型的枚舉值應以大寫字母開頭。

類型修訂和版本:

許多類型註冊功能要求爲註冊的類型指定版本。 類型修訂和版本允許新屬性或方法存在於新版本中,同時保持與先前版本的兼容性。

考慮以下兩個QML文件:

// main.qml
import QtQuick 1.0

Item {
    id: root
    MyType {}
}

 

// MyType.qml
import MyTypes 1.0

CppType {
    value: root.x
}

 

其中CppType映射到C ++類CppType。

如果CppType的作者在其類型定義的新版本中將root屬性添加到CppType,則root.x現在將解析爲其他值,因爲root也是頂級組件的ID。 作者可以指定可以從特定的次要版本獲得新的根屬性。 這允許在不破壞現有程序的情況下將新屬性和功能添加到現有類型。

REVISION標記用於將根屬性標記爲該類型的修訂版1中添加的。 也可以使用Q_REVISION(x)宏將諸如Q_INVOKABLE,信號和插槽之類的方法標記爲修訂版本:

class CppType : public BaseType
{
    Q_OBJECT
    Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION 1)

signals:
    Q_REVISION(1) void rootChanged();
};

要將新的類修訂註冊到特定版本,請使用以下功能:

template<typename T, int metaObjectRevision>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

要爲MyTypes 1.1註冊CppType版本1:

qmlRegisterType<CppType,1>("MyTypes", 1, 1, "CppType")

root僅在導入MyTypes 1.1版時可用。

出於相同的原因,在更高版本中引入的新類型應使用qmlRegisterType的次要版本參數。

該語言的功能允許在不破壞現有應用程序的情況下進行行爲更改。 因此,QML模塊的作者應始終記住記錄次要版本之間的更改,並且QML模塊的用戶應在部署更新的import語句之前檢查其應用程序是否仍正常運行。

您還可以使用qmlRegisterRevision()函數註冊您的類型所依賴的基類的修訂版:

template<typename T, int metaObjectRevision>
int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)

template<typename T, int metaObjectRevision>
int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)

template<typename T, typename E, int metaObjectRevision>
int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)

例如,如果更改了BaseType並且現在具有修訂版1,則可以指定您的類型使用新修訂版:

qmlRegisterRevision<BaseType,1>("MyTypes", 1, 1);

當從其他作者提供的基類派生時,這很有用。 從Qt Quick模塊擴展類時。

注意:QML引擎不支持對屬性或分組和附加屬性對象的信號的修訂。

註冊擴展對象:

將現有的類和技術集成到QML中時,通常需要對API進行調整,以使其更好地適應聲明性環境。 儘管通常可以通過直接修改原始類來獲得最佳結果,但是如果這不可能或由於其他一些原因而變得複雜,則擴展對象無需進行直接修改就可以實現有限的擴展可能性。

擴展對象將其他屬性添加到現有類型。 擴展對象只能添加屬性,不能添加信號或方法。 擴展類型定義允許程序員在註冊類時提供其他類型,稱爲擴展類型。 從QML內部使用時,這些屬性與原始目標類透明合併。 例如:

QLineEdit {
    leftMargin: 20
}

leftMargin屬性是添加到現有C ++類型QLineEdit的新屬性,而無需修改其源代碼。

qmlRegisterExtendedType()函數用於註冊擴展類型。 請注意,它有兩種形式。

template<typename T, typename ExtendedT>
int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

template<typename T, typename ExtendedT>
int qmlRegisterExtendedType()

應該使用此函數代替常規的qmlRegisterType()變體。 這些參數與相應的非擴展註冊函數相同,除了ExtendedT參數是擴展對象的類型之外。

擴展類是常規QObject,其構造函數採用QObject指針。 但是,擴展類的創建被延遲,直到訪問第一個擴展屬性。 創建擴展類,並將目標對象作爲父級傳遞。 訪問原始屬性時,將使用擴展對象上的相應屬性。

擴展對象示例演示了擴展對象的用法。[https://doc.qt.io/qt-5/qtqml-referenceexamples-extended-example.html]

定義特定於QML的類型和屬性:

提供附加屬性:

在QML語言語法中,存在附加屬性和附加信號處理程序的概念,它們是附加到對象的附加屬性。 本質上,此類屬性是通過附加類型實現和提供的,並且這些屬性可以附加到其他類型的對象。 這與由對象類型本身(或對象的繼承類型)提供的普通對象屬性形成對比。

例如,下面的項目使用附加的屬性和附加的處理程序:

import QtQuick 2.0

Item {
    width: 100; height: 100

    focus: true
    Keys.enabled: false
    Keys.onReturnPressed: console.log("Return key was pressed")
}

在這裏,Item對象能夠訪問和設置Keys.enabled和Keys.onReturnPressed的值。 這允許Item對象訪問這些額外的屬性,作爲對其自身現有屬性的擴展。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章