Преобразование типов данных между QML и C++
При обмене значениями данных между QML и C++, движок QML преобразует их в правильные типы данных, подходящие для использования в QML или C++. Это требует, чтобы данные были типа, распознаваемого движком.
Движок QML предоставляет встроенную поддержку большого числа типов данных Qt C++. Кроме того, пользовательские типы C++ могут быть зарегистрированы в системе типов QML, чтобы сделать их доступными для движка.
На этой странице обсуждаются типы данных, поддерживаемые движком QML, и способы их преобразования между QML и C++.
Владение данными
При передаче данных из C++ в QML владение данными всегда остается у C++. Исключением из этого правила является случай, когда объект QObject возвращается из явного вызова метода C++: в этом случае движок QML принимает на себя владение объектом, если только владение объектом явно не установлено за C++ путём вызова QQmlEngine::setObjectOwnership() со значением QQmlEngine::CppOwnership.
Кроме того, движок QML учитывает стандартную семантику наследования родительского владения объектами QObject в Qt C++, и никогда не будет удалять экземпляр QObject, у которого есть родитель.
Основные типы данных Qt
По умолчанию QML распознает следующие типы данных Qt, которые автоматически преобразуются в соответствующий базовый тип QML при передаче из C++ в 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() или Q_ENUMS() | enumeration |
Примечание: Классы, предоставляемые модулем Qt GUI, такие как QColor, QFont, QQuaternion и QMatrix4x4, доступны из QML только при включении модуля Qt Quick.
Для удобства многие из этих типов могут быть указаны в QML строковыми значениями или с помощью связанного метода, предоставленного объектом QtQml::Qt. Например, свойство Image::sourceSize имеет тип size (который автоматически преобразуется в тип QSize) и может быть указан строковым значением, отформатированным как "widthxheight", или с помощью функции Qt.size():
Item {
Image { sourceSize: "100x200" }
Image { sourceSize: Qt.size(100, 200) }
} Дополнительную информацию см. в документации к каждому отдельному типу в разделе QML Basic Types.
Типы, производные от QObject
Любой класс, производный от QObject, может быть использован как тип для обмена данными между QML и C++, при условии, что класс был зарегистрирован в системе типов QML.
Движок позволяет регистрировать как экземплярируемые, так и неэкземплярируемые типы. После регистрации класса как типа QML он может быть использован как тип данных для обмена данными между QML и C++. Подробную информацию о регистрации типов см. в разделе Регистрация типов C++ в системе типов QML.
Преобразование между типами Qt и JavaScript
Движок QML имеет встроенную поддержку преобразования ряда типов Qt в соответствующие типы JavaScript и наоборот при передаче данных между QML и C++. Это позволяет использовать эти типы и получать их в C++ или JavaScript без необходимости реализации пользовательских типов, обеспечивающих доступ к значениям данных и их атрибутам.
(Обратите внимание, что среда JavaScript в QML модифицирует прототипы родных объектов 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 и автоматически преобразуется в QVariantList или QVariantMap при передаче в C++.
QDateTime в JavaScript Date
Движок QML предоставляет автоматическое преобразование между значениями QDateTime и объектами JavaScript Date.
Например, функция, определённая в QML ниже, принимает объект JavaScript Date, и возвращает новый объект Date с текущей датой и временем. Код C++ ниже вызывает эту функцию, передавая значение QDateTime, которое автоматически преобразуется движком в объект Date при передаче в функцию readDate(). В свою очередь, функция readDate() возвращает объект Date, который автоматически преобразуется в значение QDateTime при получении в 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; |
Аналогично, если тип C++ использует QDateTime для типа свойства или параметра метода, значение может быть создано как объект JavaScript Date в QML и автоматически преобразуется в значение QDateTime при передаче в C++.
QTime и JavaScript Date
Движок QML предоставляет автоматическое преобразование значений QTime в объекты JavaScript Date. Поскольку значения QTime не содержат компонента даты, при преобразовании создаётся компонент даты. Поэтому не следует полагаться на компонент даты результирующего объекта Date.
Внутри движок выполняет преобразование из объекта JavaScript Date в QTime, преобразуя его в объект QDateTime и вызывая его метод time().
Тип последовательности в JavaScript массив
Некоторые типы последовательностей C++ прозрачно поддерживаются в QML, ведя себя как типы JavaScript Array.
В частности, QML в настоящее время поддерживает:
QList<int>QList<qreal>QList<bool>-
QList<QString>и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.
END_OF_DOCUMENT_MARKERЕсли последовательность возвращается из функции с Q_INVOKABLE, доступ и изменение значительно дешевле, так как не происходит чтение или запись свойств объекта QObject; вместо этого напрямую осуществляется доступ и изменение данных последовательности на C++.
В обоих случаях — Q_PROPERTY и возвращение из Q_INVOKABLE — элементы std::vector копируются. Эта операция копирования может быть дорогостоящей, поэтому следует использовать std::vector с осторожностью.
Другие типы последовательностей не поддерживаются прозрачно, и вместо этого экземпляр любого другого типа последовательности будет передаваться между QML и C++ как неявный объект QVariantList.
Важно: Существуют некоторые незначительные различия в семантике таких типов последовательностей Array и стандартных типов JavaScript Array, которые обусловлены использованием типа хранения C++ в реализации. В частности, удаление элемента из массива приведет к замене этого элемента значением по умолчанию, а не значением Undefined. Аналогично, установка свойства length массива на значение, большее, чем его текущее значение, приведет к дополнению массива до указанной длины элементами по умолчанию, а не элементами Undefined. Наконец, контейнеры Qt поддерживают целые индексы со знаком (а не без знака), поэтому попытка доступа к индексу, превышающему INT_MAX, завершится ошибкой.
Значения по умолчанию для каждого типа последовательности следующие:
| QList<int> | целое значение 0 |
| QList<qreal> | вещественное значение 0,0 |
| QList<bool> | булево значение false
|
| QList<QString> и QStringList | пустая QString |
| QVector<QString> | пустая QString |
| std::vector<QString> | пустая QString |
| QList<QUrl> | пустая QUrl |
| QVector<QUrl> | пустая QUrl |
| std::vector<QUrl> | пустая QUrl |
| QVector<int> | целое значение 0 |
| QVector<qreal> | вещественное значение 0,0 |
| QVector<bool> | булево значение false
|
| std::vector<int> | целое значение 0 |
| std::vector<qreal> | вещественное значение 0,0 |
| std::vector<bool> | булево значение false
|
Если вы хотите удалить элементы из последовательности, а не просто заменить их значениями по умолчанию, не используйте оператор удаления по индексу ("delete sequence[i]"), а используйте функцию splice ("sequence.splice(startIndex, deleteCount)").
QByteArray к JavaScript ArrayBuffer
Двигатель QML предоставляет автоматическую конвертацию типов между значениями QByteArray и JavaScript ArrayBuffer объектами.
Типы значений
Некоторые типы значений в Qt, такие как QPoint, представлены в JavaScript как объекты, имеющие те же свойства и функции, что и в API C++. Такое же представление возможно для пользовательских типов C++. Для включения пользовательского типа значения в движок QML необходимо добавить аннотацию Q_GADGET к объявлению класса. Свойства, которые должны быть видимыми в представлении JavaScript, необходимо объявить с помощью Q_PROPERTY. Аналогично, функции должны быть помечены с помощью Q_INVOKABLE. Это также относится к API C++ на основе QObject. Например, класс Actor ниже аннотирован как gadget и имеет свойства:
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, его перечисление 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
};
}; Типы перечислений в качестве параметров сигналов и методов
Сигналы и методы C++ с параметрами типа перечисления могут использоваться из QML, при условии, что перечисление и сигнал или метод объявлены в одном классе, или что значение перечисления является одним из объявленных в Пространстве имён Qt.
Кроме того, если сигнал C++ с параметром типа перечисления должен быть связан с функцией QML с помощью функции connect(), тип перечисления необходимо зарегистрировать с помощью qRegisterMetaType().
Для сигналов QML значения перечислений могут передаваться в качестве параметров сигналов с помощью типа int:
Message {
signal someOtherSignal(int statusValue)
Component.onCompleted: {
someOtherSignal(Message.Loading)
}
}
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.13/qtqml-cppintegration-data.html