When data values are exchanged between QML and C++, they are converted by the QML engine to have the correct data types as appropriate for use in QML or C++. This requires the exchanged data to be of a type that is recognizable by the engine.
The QML engine provides built-in support for a large number of Qt C++ data types. Additionally, custom C++ types may be registered with the QML type system to make them available to the engine.
This page discusses the data types supported by the QML engine and how they are converted between QML and C++.
When data is transferred from C++ to QML, the ownership of the data always remains with C++. The exception to this rule is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object, unless the ownership of the object has explicitly been set to remain with C++ by invoking QQmlEngine::setObjectOwnership () 采用 QQmlEngine::CppOwnership 指定。
Additionally, the QML engine respects the normal QObject parent ownership semantics of Qt C++ objects, and will never delete a QObject instance which has a parent.
By default, QML recognizes the following Qt data types, which are automatically converted to a corresponding QML basic type when passed from C++ to QML and vice-versa:
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 |
Enums declared with Q_ENUM () or Q_ENUMS() | enumeration |
注意: Classes provided by the Qt GUI module, such as QColor , QFont , QQuaternion and QMatrix4x4 , are only available from QML when the Qt Quick module is included.
As a convenience, many of these types can be specified in QML by string values, or by a related method provided by the
QtQml::Qt
object. For example, the
Image::sourceSize
property is of type
size
(which automatically translates to the
QSize
type) and can be specified by a string value formatted as "width
x
height", or by the Qt.
size
() 函数:
Item { Image { sourceSize: "100x200" } Image { sourceSize: Qt.size(100, 200) } }
See documentation for each individual type under QML 基本类型 了解更多信息。
任何 QObject -derived class may be used as a type for the exchange of data between QML and C++, providing the class has been registered with the QML type system.
The engine allows the registration of both instantiable and non-instantiable types. Once a class is registered as a QML type, it can be used as a data type for exchanging data between QML and C++. See Registering C++ types with the QML type system for further details on type registration.
The QML engine has built-in support for converting a number of Qt types to related JavaScript types, and vice-versa, when transferring data between QML and C++. This makes it possible to use these types and receive them in C++ or JavaScript without needing to implement custom types that provide access to the data values and their attributes.
(Note that the JavaScript environment in QML modifies native JavaScript object prototypes, including those of
字符串
,
Date
and
Number
, to provide additional features. See the
JavaScript 主机环境
for further details.)
The QML engine provides automatic type conversion between QVariantList and JavaScript arrays, and between QVariantMap and JavaScript objects.
For example, the function defined in QML below expects two arguments, an array and an object, and prints their contents using the standard JavaScript syntax for array and object item access. The C++ code below calls this function, passing a QVariantList 和 QVariantMap , which are automatically converted to JavaScript array and object values, repectively:
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))); |
This produces output like:
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)
Similarly, if a C++ type uses a QVariantList or QVariantMap type for a property type or method parameter, the value can be created as a JavaScript array or object in QML, and is automatically converted to a QVariantList or QVariantMap when it is passed to C++.
Mind that
QVariantList
and
QVariantMap
properties of C++ types are stored as values and cannot be changed in place by QML code. You can only replace the whole map or list, but not manipulate its contents. The following code does not work if the property
l
是
QVariantList
:
MyListExposingItem { l: [1, 2, 3] Component.onCompleted: l[0] = 10 }
The following code does work:
MyListExposingItem { l: [1, 2, 3] Component.onCompleted: l = [10, 2, 3] }
The QML engine provides automatic type conversion between
QDateTime
values and JavaScript
Date
对象。
For example, the function defined in QML below expects a JavaScript
Date
object, and also returns a new
Date
object with the current date and time. The C++ code below calls this function, passing a
QDateTime
value that is automatically converted by the engine into a
Date
object when it is passed to the
readDate()
function. In turn, the readDate() function returns a
Date
object that is automatically converted into a
QDateTime
value when it is received in C++:
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; |
Similarly, if a C++ type uses a
QDateTime
for a property type or method parameter, the value can be created as a JavaScript
Date
object in QML, and is automatically converted to a
QDateTime
value when it is passed to C++.
The QML engine provides automatic type conversion from
QTime
values to JavaScript
Date
objects. As
QTime
values do not contain a date component, one is created for the conversion only. Thus, you should not rely on the date component of the resulting Date object.
Under the hood, conversion from a JavaScript
Date
对象到
QTime
is done by converting to a
QDateTime
object and calling its
time()
方法。
Certain C++ sequence types are supported transparently in QML to behave like JavaScript
数组
类型。
In particular, QML currently supports:
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>
and all registered QList , QVector , QQueue , QStack , QSet , QLinkedList, std::list, std::vector that contain a type marked with Q_DECLARE_METATYPE .
These sequence types are implemented directly in terms of the underlying C++ sequence. There are two ways in which such sequences can be exposed to QML: as a Q_PROPERTY of the given sequence type; or as the return type of a Q_INVOKABLE method. There are some differences in the way these are implemented, which are important to note.
If the sequence is exposed as a Q_PROPERTY , accessing any value in the sequence by index will cause the sequence data to be read from the QObject 's property, then a read to occur. Similarly, modifying any value in the sequence will cause the sequence data to be read, and then the modification will be performed and the modified sequence will be written back to the QObject 's property.
If the sequence is returned from a Q_INVOKABLE function, access and mutation is much cheaper, as no QObject property read or write occurs; instead, the C++ sequence data is accessed and modified directly.
In both the Q_PROPERTY and return from Q_INVOKABLE cases, the elements of a std::vector are copied. This copying may be an expensive operation, so std::vector should be used judiciously.
You can also create a list-like data structure by constructing a QJSValue 使用 QJSEngine::newArray (). Such a JavaScript array does not need any conversion when passing it between QML and C++. See QJSValue#Working With Arrays for details on how to manipulate JavaScript arrays from C++.
Other sequence types are not supported transparently, and instead an instance of any other sequence type will be passed between QML and C++ as an opaque QVariantList .
Important Note: There are some minor differences between the semantics of such sequence Array types and default JavaScript Array types which result from the use of a C++ storage type in the implementation. In particular, deleting an element from an Array will result in a default-constructed value replacing that element, rather than an Undefined value. Similarly, setting the length property of the Array to a value larger than its current value will result in the Array being padded out to the specified length with default-constructed elements rather than Undefined elements. Finally, the Qt container classes support signed (rather than unsigned) integer indexes; thus, attempting to access any index greater than INT_MAX will fail.
The default-constructed values for each sequence type are as follows:
QList <int> | integer value 0 |
QList <qreal> | real value 0.0 |
QList <bool> |
boolean value
false
|
QList < QString > 和 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
|
If you wish to remove elements from a sequence rather than simply replace them with default constructed values, do not use the indexed delete operator ("delete sequence[i]") but instead use the
splice
function ("sequence.splice(startIndex, deleteCount)").
The QML engine provides automatic type conversion between
QByteArray
values and JavaScript
ArrayBuffer
对象。
Some value types in Qt such as
QPoint
are represented in JavaScript as objects that have the same properties and functions like in the C++ API. The same representation is possible with custom C++ value types. To enable a custom value type with the QML engine, the class declaration needs to be annotated with
Q_GADGET
. Properties that are intended to be visible in the JavaScript representation need to be declared with
Q_PROPERTY
. Similarly functions need to be marked with
Q_INVOKABLE
. This is the same with
QObject
based C++ APIs. For example, the
Actor
class below is annotated as gadget and has properties:
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)
The usual pattern is to use a gadget class as the type of a property, or to emit a gadget as a signal argument. In such cases, the gadget instance is passed by value between C++ and QML (because it's a value type). If QML code changes a property of a gadget property, the entire gadget is re-created and passed back to the C++ property setter. In Qt 5, gadget types cannot be instantiated by direct declaration in QML. In contrast, a QObject instance can be declared; and QObject instances are always passed by pointer from C++ to QML.
To use a custom enumeration as a data type, its class must be registered and the enumeration must also be declared with
Q_ENUM
() to register it with Qt's meta object system. For example, the
Message
class below has a
状态
枚举:
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
class has been
registered
with the QML type system, its
状态
enum can be used from QML:
Message { onStatusChanged: { if (status == Message.Ready) console.log("Message is loaded!") } }
To use an enum as a flags type in QML, see Q_FLAG ().
注意: The names of enum values must begin with a capital letter in order to be accessible from QML.
... enum class Status { Ready, Loading, Error } Q_ENUM(Status) ...
Enum classes are registered in QML as scoped and unscoped properties. The
Ready
value will be registered at
Message.Status.Ready
and
Message.Ready
.
When using enum classes, there can be multiple enums using the same identifiers. The unscoped registration will be overwriten by the last registered enum. For classes that contain such name conficts it is possible to disable the unscoped registration by annotating your class with a special
Q_CLASSINFO
macro. Use the name
RegisterEnumClassesUnscoped
采用值
false
to prevent scoped enums from being merged into the same name space.
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 }; };
C++ signals and methods with enumeration-type parameters can be used from QML provided that the enumeration and the signal or method are both declared within the same class, or that the enumeration value is one of those declared in the Qt 名称空间 .
Additionally, if a C++ signal with an enum parameter should be connectable to a QML function using the connect() function, the enum type must be registered using qRegisterMetaType ().
For QML signals, enum values may be passed as signal parameters using the
int
类型:
Message { signal someOtherSignal(int statusValue) Component.onCompleted: { someOtherSignal(Message.Loading) } }