Преобразование типов данных между 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.
Типы, производные от 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>
Эти типы последовательностей реализованы непосредственно в терминах базовых C++ последовательностей. Существует два способа экспонирования таких последовательностей в QML: как свойство Q_PROPERTY данного типа последовательности или как возвращаемый тип вызываемого метода Q_INVOKABLE.
Если последовательность экспонирована как Q_PROPERTY, обращение к любому значению в последовательности по индексу приведет к чтению данных последовательности из свойства объекта QObject, а затем к чтению. Аналогично, изменение любого значения в последовательности приведет к чтению данных последовательности, выполнению изменения и записи измененной последовательности обратно в свойство объекта QObject.
Если последовательность возвращается функцией Q_INVOKABLE, доступ и изменение намного дешевле, так как не происходит чтение или запись свойства объекта QObject; вместо этого данные последовательности C++ обращаются и изменяются непосредственно.
В обоих случаях (свойство Q_PROPERTY и возвращение от Q_INVOKABLE), элементы std::vector копируются. Эта копия может быть дорогостоящей операцией, поэтому следует использовать std::vector разумно.
END_OF_DOCUMENT_MARKERДругие типы последовательностей не поддерживаются прозрачно, а вместо этого экземпляр любого другого типа последовательности будет передаваться между QML и C++ как непросветленный QVariantList.
Важно: Существуют некоторые незначительные различия между семантикой таких типов массивов последовательности и стандартных типов массивов JavaScript, которые возникают из-за использования типа хранения 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.12/qtqml-cppintegration-data.html