Преобразование типов данных между 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 для типа свойства или параметра метода, значение может быть создано в QML как массив или объект JavaScript и автоматически преобразуется в 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 для типа свойства или параметра метода, значение может быть создано в QML как объект JavaScript Date и автоматически преобразуется в значение 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.
Вы также можете создать структуру данных, похожую на список, создав QJSValue с помощью QJSEngine::newArray(). Такому массиву JavaScript не требуется никакое преобразование при передаче между QML и C++. Подробности о манипулировании массивами JavaScript из C++ см. в QJSValue#Работа с массивами.
Другие типы последовательностей не поддерживаются прозрачно и вместо этого любой другой тип последовательности будет передан между 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. Это то же самое для C++ API на основе 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) Обычная практика — использовать класс gadget в качестве типа свойства или передавать gadget как аргумент сигнала. В таких случаях экземпляр gadget передаётся по значению между C++ и QML (потому что это тип значения). Если QML-код изменяет свойство свойства gadget, весь gadget пересоздаётся и передаётся обратно в метод установки свойства C++. В Qt 5 типы gadget не могут быть созданы непосредственным объявлением в 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
};
}; Типы перечислений в качестве параметров сигналов и методов
Сигналы и методы 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.15/qtqml-cppintegration-data.html