在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)
}
}