在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 |
QPoint, QPointF | point |
QSize, QSizeF | size |
QRect, QRectF | rect |
QMatrix4x4 | matrix4x4 |
QQuaternion | quaternion |
QVector2D, QVector3D, QVector4D | vector2d, vector3d, vector4d |
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>
andQStringList
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 |
如果您希望從序列中刪除元素,而不是簡單地用默認構造值替換它們,請不要使用索引的刪除運算符(“ 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)
}
}