Spec-Zone.ru › Qt

Преобразование типов данных между QML и C++

При обмене значениями данных между QML и C++, движок QML преобразует их к соответствующим типам данных, подходящим для использования в QML или C++. Это требует, чтобы передаваемые данные были типа, распознаваемого движком.

Движок QML предоставляет встроенную поддержку большого числа типов данных Qt C++. Кроме того, пользовательские типы C++ могут быть зарегистрированы в системе типов QML, чтобы сделать их доступными для движка.

На этой странице обсуждаются типы данных, поддерживаемые движком QML, и как они преобразуются между QML и C++.

Владение данными

При передаче данных из C++ в QML владение данными всегда остается у C++. Исключением из этого правила является случай, когда из явного вызова метода C++ возвращается QObject: в этом случае движок QML берет на себя владение объектом, если только владение объектом явно не было установлено за C++ путём вызова QQmlEngine::setObjectOwnership() со значением QQmlEngine::CppOwnership.

Кроме того, движок QML соблюдает стандартные семантики владения родительским объектом для объектов Qt C++ QObject и никогда не удаляет экземпляр 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++.

Обратите внимание, что свойства 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++.

Примечание: Обратите внимание на разницу в нумерации месяцев: JavaScript нумерует январь с 0 до 11 для декабря, что отличается от нумерации Qt, где январь — 1, а декабрь — 12.

Примечание: При использовании строки в JavaScript в качестве значения объекта Date, обратите внимание, что строка без полей времени (то есть простая дата) интерпретируется как начало соответствующего дня в UTC, в отличие от new Date(y, m, d), использующего начало дня по локальному времени. Большинство других способов построения объекта Date в JavaScript производят локальное время, если только не используются методы с UTC в их именах. Если ваша программа выполняется в часовом поясе, отстающем от UTC (в основном к западу от Гринвичского меридиана), использование только даты приведет к объекту Date, чей getDate() на один меньше, чем число дня в вашей строке; он обычно будет иметь большое значение для getHours(). Варианты методов с UTC, getUTCDate() и getUTCHours(), дадут ожидаемые результаты для таких объектов Date. См. также следующий раздел.

QDate и JavaScript Date

Движок QML автоматически преобразует QDate в тип JavaScript Date , представляя дату началом дня в UTC. Дата отображается обратно в QDate через QDateTime, выбирая его метод date(), используя локальное время, если UTC форма совпадает с началом следующего дня, в этом случае используется UTC форма.

Это несколько необычное решение является обходным путем для того факта, что JavaScript при построении объекта Date из строки с датой использует начало дня в UTC, а new Date(y, m, d) использует начало дня по локальному времени, как обсуждалось в примечании в конце предыдущего раздела.

В результате, при раскрытии свойства QDate в QML следует быть осторожным при чтении его значения: методы Date.getUTCFullYear(), Date.getUTCMonth() и Date.getUTCDate() с большей вероятностью предоставят ожидаемые результаты, чем соответствующие методы без UTC в их именах.

END_OF_DOCUMENT_MARKER

Поэтому обычно более надёжно использовать свойство QDateTime. Это позволяет контролировать, в терминах UTC или местного времени задаётся дата (и время) со стороны QDateTime; при условии, что код JavaScript написан для работы с тем же стандартом, следует избегать проблем.

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.

Если последовательность возвращается из вызываемого метода 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. Аналогично, установка свойства длины массива на значение, большее его текущего значения, приведёт к дополнению массива до указанной длины элементами по умолчанию, а не элементами 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 ниже аннотирован как прибор и имеет свойства:

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)

Обычная схема — использование класса-прибора в качестве типа свойства или отправка прибора в качестве аргумента сигнала. В таких случаях экземпляр прибора передаётся по значению между C++ и QML (поскольку это тип значения). Если код QML изменяет свойство свойства прибора, весь прибор пересоздаётся и передаётся обратно в установщик свойства C++.

В Qt 5 типы приборов не могут быть созданы путём прямого объявления в 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.2/qtqml-cppintegration-data.html

Spec-Zone.ru

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