Spec-Zone.ru › Qt 6.1

Преобразование типов данных между 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, 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 с осторожностью.

Также можно создать структуру данных, похожую на список, создав 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. Это относится и к 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)

Обычно в качестве типа свойства используется класс 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-6.1/qtqml-cppintegration-data.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API