天天看點

4 | 将c++類型的屬性暴露給QML

Exposing Attributes of C++ Types to QML

QML可以很容易地使用c++代碼中定義的功能進行擴充。由于QML引擎與Qt元對象系統的緊密內建,任何由qobject派生類或Q_GADGET類型公開的功能都可以從QML代碼中通路。這使得c++資料和函數可以直接從QML通路,通常很少或沒有修改。

QML引擎能夠通過元對象系統内省QObject執行個體。這意味着任何QML代碼都可以通路qobject派生類執行個體的以下成員:

  • Properties
  • Methods (providing they are public slots or flagged with Q_INVOKABLE)
  • Signals

(此外,如果枚舉已經用Q_ENUM聲明,那麼它們也是可用的。有關詳細資訊,請參閱QML和c++之間的資料類型轉換。)

一般來說,無論qobject派生類是否已注冊到QML類型系統,都可以從QML通路這些類。然而,如果一個類要以一種需要引擎通路附加類型資訊的方式使用——例如,如果類本身要用作方法參數或屬性,或者它的枚舉類型之一要以這種方式使用——那麼該類可能需要注冊。建議對QML中使用的所有類型進行注冊,因為隻有注冊的類型才能在編譯時進行分析。

Q_GADGET類型需要注冊,因為它們不是從已知的公共基礎派生出來的,不能自動提供。如果沒有注冊,則無法通路它們的屬性和方法。

還要注意,本文檔中涉及的許多重要概念在使用c++編寫QML擴充教程中進行了示範。

有關c++和不同QML內建方法的更多資訊,請參閱c++和QML內建概述頁面。

Data Type Handling and Ownership 資料類型處理和所有權

從c++傳輸到QML的任何資料,無論是作為屬性值、方法參數或傳回值,還是信号參數值,都必須是QML引擎支援的類型。

預設情況下,該引擎支援許多Qt c++類型,并可以在從QML使用時自動對它們進行适當的轉換。此外,向QML類型系統注冊的c++類可以用作資料類型,如果适當地注冊了它們的枚舉,也可以用作資料類型。有關更多資訊,請參閱QML和c++之間的資料類型轉換。

此外,當資料從c++傳輸到QML時,資料所有權規則也被考慮在内。有關詳細資訊,請參閱資料所有權。

Exposing Properties

可以使用Q_PROPERTY()宏為任何qobject派生類指定屬性。屬性是具有關聯讀函數和可選寫函數的類資料成員。

qobject派生類或Q_GADGET類的所有屬性都可以從QML通路。

例如,下面是一個帶有author屬性的Message類。正如Q_PROPERTY宏調用所指定的,這個屬性可以通過author()方法讀取,也可以通過setAuthor()方法寫入:

注意:不要使用typedef或using for Q_PROPERTY類型,因為這些會混淆moc。這可能會導緻某些類型比較失敗。

Instead of:

using FooEnum = Foo::Enum;

class Bar : public QObject {
    Q_OBJECT
    Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged)
};           

Refer to the type directly:

class Bar : public QObject {
    Q_OBJECT
    Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged)
};           
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;
};           

如果在加載名為MyItem的檔案時将該類的執行個體設定為上下文屬性。qml從c++:

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();
}           

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

// 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的一個基本特性,它通過在屬性的任何依賴項值發生變化時自動更新屬性來加強屬性之間的關系。

在上面的例子中,author屬性的相關NOTIFY信号是authorChanged,這是在Q_PROPERTY()宏調用中指定的。這意味着無論何時發出信号——就像在Message::setAuthor()中作者更改時一樣——都會通知QML引擎必須更新涉及author屬性的任何綁定,然後,引擎将通過再次調用Message::author()來更新文本屬性。

如果author屬性是可寫的,但沒有相關的NOTIFY信号,則文本值将使用Message::author()傳回的初始值進行初始化,但不會随着對該屬性的任何後續更改而更新。此外,任何試圖從QML綁定到屬性的嘗試都将從引擎産生運作時警告。

注意:建議将NOTIFY信号命名為<property>修改了<property>為屬性的名稱。QML引擎生成的相關屬性更改信号處理程式總是采用< property >Changed的形式,而不考慮相關c++信号的名稱,是以建議信号名稱遵循此約定以避免任何混淆。

參考連結:

https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html