Преобразование типов данных между 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++.
Обратите внимание, что свойства QVariantList и QVariantMap типов C++ хранятся как значения и не могут быть изменены на месте кодом 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, которое автоматически преобразуется движком в объект 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.
Если последовательность возвращается из функции 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.14/qtqml-cppintegration-data.html