QML與C++之間的數據類型轉換

在QML和C ++之間交換數據值時,它們將由QML引擎轉換爲具有適用於QML或C ++的正確數據類型。 這要求交換的數據具有引擎可識別的類型。

QML引擎爲大量QT C++數據類型提供內置支持。此外,自定義C++類型可以註冊到QML類型系統中,使它們可以用於引擎。

該頁面討論了QML引擎支持的數據類型以及如何在QML和C ++之間轉換它們。

數據所有權

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

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

基本Qt數據類型

默認情況下,QML識別以下Qt數據類型,當從C ++傳遞到QML時,它們會自動轉換爲相應的QML基本類型,反之亦然:

Qt Type QML Basic Type
bool bool
unsigned int, int int
double double
float, qreal real
QString string
QUrl url
QColor color
QFont font
QDateTime date
QPointQPointF point
QSizeQSizeF size
QRectQRectF rect
QMatrix4x4 matrix4x4
QQuaternion quaternion
QVector2DQVector3DQVector4D vector2dvector3dvector4d
Enums declared with Q_ENUM() or Q_ENUMS() enumeration

注意:Qt GUI模塊提供的類,例如QColor,QFont,QQuaternion和QMatrix4x4,僅當包含Qt Quick模塊時纔可從QML獲得。

爲方便起見,可以在QML中通過字符串值或QtQml :: Qt對象提供的相關方法來指定其中許多類型。 例如,Image :: sourceSize屬性的大小爲size(將自動轉換爲QSize類型),並且可以由格式爲“ widthxheight”的字符串值或Qt.size()函數指定:

Item {
    Image { sourceSize: "100x200" }
    Image { sourceSize: Qt.size(100, 200) }
}

有關更多信息,請參見QML基本類型下有關每種類型的文檔。

QObject派生的類型

可以將任何QObject派生的類用作在QML和C ++之間交換數據的類型,前提是該類已在QML類型系統中註冊。

引擎允許同時註冊可實例化和不可實例化類型。一旦一個類被註冊爲QML類型,它就可以用作QML和C++之間數據交換的數據類型。有關類型註冊的詳細信息,請參見使用QML類型系統註冊C++類型

Qt和JavaScript類型之間的轉換

當在QML和C ++之間傳輸數據時,QML引擎具有內置支持,可將多種Qt類型轉換爲相關的JavaScript類型,反之亦然。 這樣就可以使用這些類型並以C ++或JavaScript接收它們,而無需實現提供對數據值及其屬性的訪問的自定義類型。

(請注意,QML中的JavaScript環境會修改本機JavaScript對象原型(包括String,Date和Number的原型)以提供其他功能。有關更多詳細信息,請參見JavaScript Host Environment。)

QVariantList和QVariantMap到JavaScript數組和對象

QML引擎提供QVariantList與JavaScript數組之間以及QVariantMap與JavaScript對象之間的自動類型轉換。

例如,以下QML中定義的函數需要兩個參數,即數組和對象,並使用標準JavaScript語法打印它們的內容以訪問數組和對象項。 下面的C ++代碼調用此函數,傳遞一個QVariantList和QVariantMap,它們分別自動轉換爲JavaScript數組和對象值:

// MyItem.qml
Item {
    function readValues(anArray, anObject) {
        for (var i=0; i<anArray.length; i++)
            console.log("Array item:", anArray[i])

        for (var prop in anObject) {
            console.log("Object item:", prop, "=", anObject[prop])
        }
    }
}

 

// C++
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));

QVariantList list;
list << 10 << QColor(Qt::green) << "bottles";

QVariantMap map;
map.insert("language", "QML");
map.insert("released", QDate(2010, 9, 21));

QMetaObject::invokeMethod(view.rootObject(), "readValues",
        Q_ARG(QVariant, QVariant::fromValue(list)),
        Q_ARG(QVariant, QVariant::fromValue(map)));

產生如下輸出:

Array item: 10
Array item: #00ff00
Array item: bottles
Object item: language = QML
Object item: released = Tue Sep 21 2010 00:00:00 GMT+1000 (EST)

同樣,如果C ++類型將QVariantList或QVariantMap類型用作屬性類型或方法參數,則可以將值創建爲QML中的JavaScript數組或對象,並在將其傳遞給C ++後自動轉換爲QVariantList或QVariantMap。

 

QDateTime到JavaScript日期

QML引擎提供QDateTime值和JavaScript Date對象之間的自動類型轉換。

例如,以下QML中定義的函數需要一個JavaScript Date對象,並且還返回帶有當前日期和時間的新Date對象。 下面的C ++代碼調用此函數,將QDateTime值傳遞給引擎,當它傳遞給readDate()函數時,該值會由引擎自動轉換爲Date對象。 反過來,readDate()函數返回一個Date對象,當在C ++中收到它時,該對象將自動轉換爲QDateTime值:

// MyItem.qml
Item {
    function readDate(dt) {
        console.log("The given date is:", dt.toUTCString());
        return new Date();
    }
}
// C++
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));

QDateTime dateTime = QDateTime::currentDateTime();
QDateTime retValue;

QMetaObject::invokeMethod(view.rootObject(), "readDate",
        Q_RETURN_ARG(QVariant, retValue),
        Q_ARG(QVariant, QVariant::fromValue(dateTime)));

qDebug() << "Value returned from readDate():" << retValue;

 

同樣,如果C ++類型使用QDateTime作爲屬性類型或方法參數,則可以在QML中將該值創建爲JavaScript Date對象,並在將其傳遞給C ++時自動將其轉換爲QDateTime值。

QTime和JavaScript日期

QML引擎提供了從QTime值到JavaScript Date對象的自動類型轉換。 由於QTime值不包含日期成分,因此僅爲轉換創建日期成分。 因此,您不應依賴於生成的Date對象的date組件。

在後臺,從JavaScript Date對象到QTime的轉換是通過轉換爲QDateTime對象並調用其time()方法完成的。

序列類型到JavaScript數組

QML透明地支持某些C ++序列類型,使其行爲類似於JavaScript數組類型。

特別是,QML當前支持:

  • QList<int>
  • QList<qreal>
  • QList<bool>
  • QList<QString> and QStringList
  • QVector<QString>
  • std::vector<QString>
  • QList<QUrl>
  • QVector<QUrl>
  • std::vector<QUrl>
  • QVector<int>
  • QVector<qreal>
  • QVector<bool>
  • std::vector<int>
  • std::vector<qreal>
  • std::vector<bool>

以及所有已註冊的QList,QVector,QQueue,QStack,QSet,QLinkedList,std :: list,std :: vector,其中包含用Q_DECLARE_METATYPE標記的類型。

這些序列類型直接根據基礎C ++序列實現。 這種序列可以通過兩種方式暴露於QML:作爲給定序列類型的Q_PROPERTY; 或作爲Q_INVOKABLE方法的返回類型。 這些實現的方式存在一些差異,需要注意。

如果序列作爲Q_PROPERTY公開,則按索引訪問序列中的任何值將導致從QObject的屬性讀取序列數據,然後進行讀取。 同樣,修改序列中的任何值都將導致讀取序列數據,然後將執行修改並將修改後的序列寫回到QObject的屬性。

如果該序列是從Q_INVOKABLE函數返回的,則訪問和更改都便宜得多,因爲不會發生讀取或寫入QObject屬性的情況; 而是直接訪問和修改C ++序列數據。

在Q_PROPERTY和從Q_INVOKABLE返回的情況下,都將複製std :: vector的元素。 這種複製可能是一項昂貴的操作,因此應謹慎使用std :: vector。

不透明地不支持其他序列類型,而是將任何其他序列類型的實例作爲不透明的QVariantList在QML和C ++之間傳遞。

重要說明:這種序列數組類型和默認JavaScript數組類型的語義之間存在一些細微差異,這是由於在實現中使用了C ++存儲類型而導致的。 特別是,從數組中刪除元素將導致默認構造的值替換該元素,而不是未定義的值。 同樣,將Array的length屬性設置爲大於其當前值的值將導致使用默認構造的元素而不是Undefined元素將Array填充爲指定的長度。 最後,Qt容器類支持帶符號(而不是無符號)的整數索引。 因此,嘗試訪問任何大於INT_MAX的索引將失敗。

每種序列類型的默認構造值如下:

QList<int> integer value 0
QList<qreal> real value 0.0
QList<bool> boolean value false
QList<QString> and QStringList empty QString
QVector<QString> empty QString
std::vector<QString> empty QString
QList<QUrl> empty QUrl
QVector<QUrl> empty QUrl
std::vector<QUrl> empty QUrl
QVector<int> integer value 0
QVector<qreal> real value 0.0
QVector<bool> boolean value false
std::vector<int> integer value 0
std::vector<qreal> real value 0.0
std::vector<bool>

boolean value false

如果您希望從序列中刪除元素,而不是簡單地用默認構造值替換它們,請不要使用索引的刪除運算符(“ delete sequence [i]”),而應使用剪接函數(“ sequence.splice(startIndex,deleteCount) )“)。

 

QByteArray轉JavaScript ArrayBuffer

QML引擎提供QByteArray值和JavaScript ArrayBuffer對象之間的自動類型轉換。

值類型(value type)

Qt中的某些值類型(例如QPoint)在JavaScript中表示爲具有與C ++ API中相同的屬性和功能的對象。 自定義C ++值類型可以使用相同的表示形式。 要使用QML引擎啓用自定義值類型,需要使用Q_GADGET註釋類聲明。 需要在JavaScript表示中可見的屬性必須使用Q_PROPERTY聲明。 類似的功能需要用Q_INVOKABLE標記。 這與基於QObject的C ++ API相同。 例如,下面的Actor類被註釋爲小工具並具有屬性:

class Actor
{
    Q_GADGET
    Q_PROPERTY(QString name READ name WRITE setName)
public:
    QString name() const { return m_name; }
    void setName(const QString &name) { m_name = name; }

private:
    QString m_name;
};

Q_DECLARE_METATYPE(Actor)

 

枚舉類型

要將自定義枚舉用作數據類型,必須註冊其類,並且還必須使用Q_ENUM()聲明該枚舉,以將其註冊到Qt的元對象系統。 例如,下面的Message類具有一個Status枚舉:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Status status READ status NOTIFY statusChanged)
public:
    enum Status {
        Ready,
        Loading,
        Error
    };
    Q_ENUM(Status)
    Status status() const;
signals:
    void statusChanged();
};

如果Message類已在QML類型系統中註冊,則可以從QML使用其Status枚舉:

Message {
     onStatusChanged: {
         if (status == Message.Ready)
             console.log("Message is loaded!")
     }
 }

要將枚舉用作QML中的標誌類型,請參見Q_FLAG()。

注意:枚舉值的名稱必須以大寫字母開頭,以便從QML進行訪問。

...
enum class Status {
          Ready,
          Loading,
          Error
}
Q_ENUM(Status)
...

枚舉類在QML中註冊爲範圍和非範圍屬性。 Ready值將在Message.Status.Ready和Message.Ready中註冊。

使用枚舉類時,可以有多個使用相同標識符的枚舉。 未註冊的註冊將被最後註冊的枚舉覆蓋。 對於包含此類名稱衝突的類,可以通過使用特殊的Q_CLASSINFO宏註釋類來禁用無範圍的註冊。 將名稱RegisterEnumClassesUnscoped與false一起使用,以防止將作用域枚舉合併到相同的名稱空間中。

class Message : public QObject
    {
        Q_OBJECT
        Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
        Q_ENUM(ScopedEnum)
        Q_ENUM(OtherValue)

    public:
        enum class ScopedEnum {
              Value1,
              Value2,
              OtherValue
        };
        enum class OtherValue {
              Value1,
              Value2
        };
    };

枚舉類型作爲信號和方法參數

如果枚舉與信號或方法都在同一類中聲明,或者枚舉值是在Qt命名空間中聲明的值之一,則可以從QML中使用帶有枚舉類型參數的C ++信號和方法。

此外,如果帶有enum參數的C ++信號應該可以使用connect()函數連接到QML函數,則必須使用qRegisterMetaType()註冊該枚舉類型。

對於QML信號,可以使用int類型將枚舉值作爲信號參數傳遞:

Message {
    signal someOtherSignal(int statusValue)

    Component.onCompleted: {
        someOtherSignal(Message.Loading)
    }
}

 

 

 

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