QML与C++之间的数据类型转换
当数据值在QML和C++之间交换时,它们将由QML引擎转换为在QML或C++中使用的数据类型。这需要交换的数据要由引擎可识别的类型。
QML引擎为大量Qt C++数据类型提供了内置支持。此外,可以通过将自定义C++类型注册到QML类型系统中,使它们对引擎可用。
有关C++和不同QML集成方法的信息,请参阅C++和QML集成概述页面。
本页讨论了QML引擎支持的数据类型以及它们如何在QML和C++之间进行转换。
数据拥有权
当数据从C++传输到QML时,数据的所有权总是属于C++。此规则的例外是在显式C++方法调用返回QObject时:在这种情况下,除非通过调用QQmlEngine::setObjectOwnership()且指定QQmlEngine::CppOwnership将对象所有权明确保留在C++,否则QML引擎假定对象所有权。
此外,QML引擎尊重Qt C++对象的正常QObject父级所有权语义,决不会删除具有父级的QObject实例。
基本Qt数据类型
默认情况下,QML识别以下Qt数据类型,当从C++传递到QML或反向传递时,它们会自动转换为对应的QML值类型
Qt类型 | QML值类型 |
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 |
使用Q_ENUM()声明的枚举 | 枚举 |
注意:由Qt GUI模块提供的类,例如QColor、QFont、QQuaternion和QMatrix4x4,只有在包含Qt Quick模块时才在QML中可用。
为了方便,这些类型中的许多可以在QML中以字符串值或通过由QtQml::Qt对象提供的相关方法来指定。例如,Image::sourceSize属性的类型为size(自动转换为QSize类型),可以指定为"宽度x高度"的格式字符串,也可以使用Qt.size()函数
有关更多信息,请参阅QML值类型下每个单独类型的文档。
QObject派生类型
任何QObject派生类都可以用作QML和C++之间数据交换的类型,前提是这个类已在QML类型系统中注册。
引擎允许注册可实例化和不可实例化的类型。一旦一个类被注册为QML类型,它就可以用作在QML和C++之间交换数据的数据类型。有关类型注册的更多详细信息,请参阅将C++类型注册到QML类型系统。
Qt和JavaScript类型之间的转换
在QML和C++之间传输数据时,QML引擎内置了对将许多Qt类型转换为相关JavaScript类型以及逆转换的支持。这使得使用这些类型并在C++或JavaScript中接收它们成为可能,而无需实现提供对数据值及其属性的访问的自定义类型。
(注意,QML中的JavaScript环境修改了原生JavaScript对象原型,包括String、Date和Number,以提供额外功能。有关详细信息,请参阅JavaScript宿主环境。)
将QVariantList和QVariantMap转换为JavaScript数组和对象
QML引擎提供了在QVariantList和JavaScript数组之间以及QVariantMap和JavaScript对象之间进行自动类型转换的功能。
例如,下面定义的QML函数预期接收两个参数,一个数组和一个对象,并使用标准的JavaScript语法打印它们的项。下面的C++代码调用此函数,传递一个QVariantList和一个QVariantMap,它们分别自动转换为JavaScript数组和对象值
QML | // 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++ | // 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类型作为属性类型或方法参数,则该值可以作为JavaScript数组或对象在QML中创建,并在传递给C++时自动转换为QVariantList或QVariantMap。
请注意,C++类型的QVariantList和QVariantMap属性以值的形式存储,并且不能通过QML代码原地修改。您只能替换整个映射或列表,而不能操作其内容。如果属性l
是一个QVariantList,下面的代码将不起作用。
MyListExposingItem { l: [1, 2, 3] Component.onCompleted: l[0] = 10 }
下面的代码是有效的
MyListExposingItem { l: [1, 2, 3] Component.onCompleted: l = [10, 2, 3] }
QDateTime转JavaScript Date
QML引擎在QDateTime值和JavaScript Date
对象之间提供自动类型转换。
例如,以下QML中定义的函数期望一个JavaScript Date
对象,并且也返回一个包含当前日期和时间的新的Date
对象。下面的C++代码调用此函数,传入一个QDateTime值,由引擎在传入readDate()
函数时自动将其转换为Date
对象。反过来,readDate()函数返回一个Date
对象,在C++中接收到时自动转换为QDateTime值。
QML | // MyItem.qml Item { function readDate(dt) { console.log("The given date is:", dt.toUTCString()); return new Date(); } } |
C++ | // 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值。
注意:注意月份编号的差异:JavaScript将1月编号为0到11(对于12月),与Qt中将1月编号为1到12(对于12月)的编号方式相差一个月。
注意:当使用JavaScript中的字符串作为Date
对象的值时,请注意,没有时间字段(因此是一个简单的日期)的字符串解释为相关天的UTC起点,与new Date(y, m, d)
相比,后者使用的是本地区时间的起点。JavaScript中构建Date
对象的大部分其他方法都会产生本地时间,除非使用带有UTC名称的方法。如果您的程序在UTC后面的区域(大致在子午线以西)中运行,则使用只有日期的字符串将导致Date
对象,其getDate()
比字符串中的日期号少一个;它通常对getHours()
有一个很大的值。这些方法的UTC变体(getUTCDate()
和getUTCHours()
)将给出您期望的结果。也请参阅下一节。
QDate和JavaScript Date
QML引擎通过将日期表示为该天的UTC起点来自动在QDate和JavaScript Date
类型之间转换。通过QDateTime将日期映射回QDate,选择其date()方法,使用日期的本地时间形式,除非它的UTC形式与下一天的开始一致,在这种情况下使用UTC形式。
这种略微诡异的安排是为了解决JavaScript从日期只包含的字符串中构建Date
对象时使用该天的UTC起点,但new Date(y, m, d)
使用的是指定期日的本地时间起点这一问题,就像在前面小节末尾注释中讨论的那样。
因此,当一个QDate属性或参数暴露给QML时,在读取其值时应该小心:与带有UTC的名称对应的方法相比,Date.getUTCFullYear()
、Date.getUTCMonth()
和Date.getUTCDate()
方法更有可能输出用户期望的结果。
因此,通常更稳健的做法是使用QDateTime属性。这可以使用户在QDateTime方面控制日期(和时间)是以UTC还是本地时间指定的;只要JavaScript代码按照相同的标准编写,应该可以避免麻烦。
QTime和JavaScript Date
QML引擎提供从QTime值到JavaScript Date
对象的自动类型转换。由于QTime值不包含日期组件,为了转换只创建一个。因此,您不应依赖于结果Date对象的日期组件。
在底层,将JavaScript中的Date
对象转换为QTime,是通过将它们转换为QDateTime对象(使用本地时间)并调用其time()方法来完成的。
序列类型到JavaScript数组的转换
有关序列类型的概述,请参见QML序列类型。QtQml模块包含一些你可能希望使用的序列类型。
您也可以通过使用QJSEngine::newArray来构造QJSValue来创建类似列表的数据结构。这种JavaScript数组在传递于QML和C++之间时不需要任何转换。请参见QJSValue#使用数组,了解如何从C++操作JavaScript数组。
QByteArray到JavaScript ArrayBuffer
QML引擎提供在QByteArray值和JavaScript ArrayBuffer
对象之间的自动类型转换。
值类型
Qt中的一些值类型,如QPoint,在JavaScript中以具有相同属性和函数的对象形式表示。同样,也可以使用自定义的C++值类型。为了使自定义值类型可用QML引擎,需要对类声明添加Q_GADGET
注释。应使用Q_PROPERTY
声明预计在JavaScript表示中可见的属性。类似地,函数需要标记为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)
常规模式是使用工具类作为属性的属性类型,或将工具作为信号参数发出。在这种情况下,工具实例在C++和QML之间按值传递(因为它是一个值类型)。如果QML代码更改了工具属性,则整个工具将被重新创建并传递回C++属性设置器。在Qt 5中,工具类型不能在QML中直接声明实例。相比之下,可以声明QObject实例;并且QObject实例总是从C++传递到QML的指针。
枚举类型
要将自定义枚举类型用作数据类型,其类必须进行注册,同时还必须使用 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 类型系统 注册,其 Status
枚举就可以在 QML 中使用了
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 }; };
相关类型的枚举通常在关联类型的范围内进行注册。例如,任何在 Q_PROPERTY 声明中使用的不同类型的枚举会导致该类型的所有枚举都可以在 QML 中使用。这通常是一个缺点而不是优点。为了避免这种情况,可以使用特殊的 Q_CLASSINFO 宏来注释你的类。使用名称 RegisterEnumsFromRelatedTypes
并将该值设置为 false
,以阻止相关类型的枚举在此类型中注册。
您应该显式注册任何想要在 QML 中使用的枚举的外部类型,使用 QML_ELEMENT 或 QML_NAMED_ELEMENT,而不是依赖于它们枚举被注入到其他类型。
class OtherType : public QObject { Q_OBJECT QML_ELEMENT public: enum SomeEnum { A, B, C }; Q_ENUM(SomeEnum) enum AnotherEnum { D, E, F }; Q_ENUM(AnotherEnum) }; class Message : public QObject { Q_OBJECT QML_ELEMENT // This would usually cause all enums from OtherType to be registered // as members of Message ... Q_PROPERTY(OtherType::SomeEnum someEnum READ someEnum CONSTANT) // ... but this way it doesn't. Q_CLASSINFO("RegisterEnumsFromRelatedTypes", "false") public: OtherType::SomeEnum someEnum() const { return OtherType::B; } };
枚举在 QML 中的作用域是很重要的。如果相关类中的枚举自动注册,则作用域是该枚举导入的类型。在上面的例子中,如果没有额外的 Q_CLASSINFO,您将使用 Message.A
,例如。如果显式注册包含枚举的 C++ 类型,并且抑制了相关类型的枚举注册,则包含枚举的 C++ 类型的 QML 类型是所有枚举的作用域。在 QML 中,您将使用 OtherType.A
而不是 Message.A
。
请注意,您可以使用 QML_FOREIGN 来注册不能修改的类型。还可以使用 QML_FOREIGN_NAMESPACE 将 C++ 类型的枚举注册到任何大写名称的 QML 命名空间中,即使该相同的 C++ 类型也被注册为 QML 值类型。
枚举类型作为信号和方法参数
只要枚举和信号或方法在同一个类中声明,或者枚举值是那些在 Qt 命名空间 中声明的值之一,C++ 信号的枚举类型参数和方法就可以从 QML 中使用。
此外,如果C++信号具有枚举参数,且应使用connect()函数将其连接到QML函数,则必须使用qRegisterMetaType()注册枚举类型。
对于QML信号,枚举值可以作为信号参数使用int
类型传递。
Message { signal someOtherSignal(int statusValue) Component.onCompleted: { someOtherSignal(Message.Loading) } }
© 2024 Qt公司。本文件所包含的文档贡献归属于各自的版权所有者。所提供的文档受GNU自由文档许可证第1.3版许可,由自由软件基金会出版。Qt及其相关标志是芬兰及其它国家和地区Qt公司的商标。所有其他商标均为其各自所有者的财产。