Определение типов QML из C++

Добавлено 11 мая 2022 в 22:22

При расширении QML кодом C++ класс C++ можно зарегистрировать в системе типов QML, чтобы этот класс можно было использовать в коде QML в качестве типа данных. Хотя свойства, методы и сигналы любого класса, производного от QObject, доступны из QML, как обсуждалось в разделе «Предоставление атрибутов типов C++ в QML», такой класс нельзя использовать в качестве типа данных из QML, пока он не будет зарегистрирован в системе типов. Кроме того, регистрация может предоставлять другие функции, такие как разрешение использовать класс в качестве объектного типа QML, который можно создать из QML, или разрешить импорт и использование из QML одиночного экземпляра класса (синглтона).

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

Обратите внимание, что ряд важных концепций, описанных в этом разделе, продемонстрирован в руководстве «Написание расширений QML с помощью C++».

ПРИМЕЧАНИЕ. Все заголовки, объявляющие типы QML, должны быть доступны без префикса из пути включения проекта.

Регистрация типов C++ в системе типов QML

Класс, производный от QObject, можно зарегистрировать в системе типов QML, чтобы разрешить использование этого типа в качестве типа данных из кода QML.

Движок позволяет регистрировать как инстанцируемые, так и неинстанцируемые (неэкземплярные) типы. Регистрация типа с возможностью создания экземпляра позволяет использовать класс C++ в качестве определения объектного типа QML, что позволяет использовать его в объявлениях объектов из кода QML для создания объектов этого типа. Регистрация также предоставляет движку дополнительные метаданные типа, позволяя использовать этот тип (и любые перечисления, объявленные классом) в качестве типа данных для значений свойств, параметров и возвращаемых значений методов, а также параметров сигналов, которыми обмениваются QML и C++.

Регистрация неэкземплярного типа также регистрирует класс как тип данных таким же образом, но этот тип не может быть использован из QML в качестве экземпляра объектного типа QML. Это полезно, например, если у типа есть перечисления, которые должны быть представлены в QML, но сам тип не должен создавать экземпляры.

Краткое руководство по выбору правильного подхода к предоставлению типов C++ для QML смотрите в разделе «Выбор правильного метода интеграции между C++ и QML».

Предварительные условия

Все упомянутые ниже макросы доступны из заголовка qqmlregistration.h. Чтобы сделать макросы доступными, вам нужно добавить следующий код в файлы, использующие их:

#include <QtQml/qqmlregistration.h>

Кроме того, ваши объявления классов должны находиться в заголовках, доступных через путь включения (include path) вашего проекта. Объявления используются во время компиляции для генерации кода регистрации, и код регистрации должен включать заголовки, содержащие объявления.

Регистрация инстанцируемого объектного типа

Любой производный от QObject класс C++ может быть зарегистрирован как определение объектного типа QML. Как только класс зарегистрирован в системе типов QML, этот класс может быть объявлен, и из кода QML может быть создан его экземпляр как для любого другого объектного типа. После создания экземпляром класса можно управлять из QML; как объясняется в разделе «Предоставление атрибутов типов C++ в QML», свойства, методы и сигналы любого класса, производного от QObject, доступны из кода QML.

Чтобы зарегистрировать класс, производный от QObject, в качестве объектного типа QML с возможностью создания экземпляра, добавьте QML_ELEMENT или QML_NAMED_ELEMENT(<name>) в объявление класса и CONFIG += qmltypes, QML_IMPORT_NAME и QML_IMPORT_MAJOR_VERSION в файл проекта. Это зарегистрирует класс в пространстве имен типов в заданной мажорной версии, используя в качестве имени типа QML либо имя класса, либо явно заданное имя. Минорные версии будут получены из любых изменений, связанных со свойствами, методами или сигналами. Минорная версия по умолчанию – 0. Вы можете явно ограничить тип, чтобы он был доступен только из определенных минорных версий, добавив в объявление класса макрос QML_ADDED_IN_MINOR_VERSION(). Клиенты могут импортировать подходящие версии пространства имен, чтобы использовать этот тип.

Например, предположим, что есть класс Message со свойствами author и createDate:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
    Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
    QML_ELEMENT
public:
    // ...
};

Этот тип можно зарегистрировать, добавив в файл проекта соответствующее пространство имен и номер версии. Например, чтобы сделать тип доступным в пространстве имен com.mycompany.messaging с версией 1.0:

CONFIG += qmltypes
QML_IMPORT_NAME = com.mycompany.messaging
QML_IMPORT_MAJOR_VERSION = 1

Если заголовок, в котором объявлен класс, недоступен из пути включения вашего проекта, вам может потребоваться изменить путь включения, чтобы можно было скомпилировать сгенерированный код регистрации:

INCLUDEPATH += com/mycompany/messaging

Этот тип можно использовать в объявлении объекта из QML, а его свойства можно читать и записывать, как показано в примере ниже:

import com.mycompany.messaging 1.0

Message {
    author: "Amelie"
    creationDate: new Date()
}

Регистрация неэкземплярных типов

Иногда класс, производный от QObject, может потребоваться зарегистрировать в системе типов QML, но не как инстанцируемый тип. Например, это тот случай, когда класс C++:

  • это тип интерфейса, который не должен создавать экземпляры;
  • это тип базового класса, который не нужно предоставлять в QML;
  • объявляет некоторое перечисление, которое должно быть доступно из QML, но класс не должен быть инстанцирован;
  • это тип, который должен предоставляться в QML через одиночный экземпляр (singleton) и не должен создавать экземпляры из QML.

Модуль Qt QML предоставляет несколько макросов для регистрации неэкземплярных типов:

  • QML_ANONYMOUS регистрирует тип C++, который не может быть создан и на который нельзя ссылаться из QML. Он позволяет движку принудительно использовать любые унаследованные типы, которые могут быть созданы из QML.
  • QML_INTERFACE регистрирует существующий тип интерфейса Qt. Этот тип не может быть создан из QML, и вы не можете объявить с ним свойства QML. Однако использование свойств C++ этого типа из QML будет выполнять ожидаемые приведения интерфейса.
  • QML_UNCREATABLE(причина) в сочетании с QML_ELEMENT или QML_NAMED_ELEMENT регистрирует именованный тип C++, который не может быть создан, но должен быть идентифицирован как тип в системе типов QML. Это полезно, если перечисления или прикрепленные свойства этого типа должны быть доступны из QML, но сам тип не должен создавать экземпляры. Параметр должен быть сообщением об ошибке, которое будет выдаваться при обнаружении попытки создания экземпляра типа.
  • QML_SINGLETON в сочетании с QML_ELEMENT или QML_NAMED_ELEMENT регистрирует тип-синглтон, который можно импортировать из QML, как описано ниже.

Обратите внимание, что все типы C++, зарегистрированные в системе типов QML, должны быть производными от QObject, даже если они не являются экземплярами.

Регистрация объектов-синглтонов с типом-синглтоном

Тип-синглтон позволяет отображать свойства, сигналы и методы в пространстве имен, не требуя от клиента ручного создания экземпляра объекта. Типы-синглтоны QObject, в частности, являются эффективным и удобным способом предоставления функциональных возможностей или значений глобальных свойств.

Обратите внимание, что типы-синглтоны не имеют связанного QQmlContext, поскольку они являются общими для всех контекстов в движке. Экземпляры типа-синглтона QObject созданы и принадлежат QQmlEngine и будут уничтожены при уничтожении движка.

С типом-синглтоном QObject можно взаимодействовать таким же образом, как и с любым другим QObject или инстанцируемым типом, за исключением того, что будет существовать только один (созданный и принадлежащий движку) экземпляр, и на него нужно ссылаться по имени типа, а не по идентификатору. К функциям Q_PROPERTY типов-синглтонов QObject можно привязаться, а функции Q_INVOKABLE API модуля QObject можно использовать в выражениях обработчика сигналов. Это делает типы-синглтоны идеальным способом реализации стилей или тем, и их также можно использовать вместо импорта скрипта .pragma library для хранения глобального состояния или предоставления глобальных функций.

После регистрации тип-синглтон QObject можно импортировать и использовать как любой другой экземпляр QObject, доступный для QML. В следующем примере предполагается, что тип-синглтон QObject был зарегистрирован в пространстве имен MyThemeModule с версией 1.0, где этот QObject имеет свойство Q_PROPERTY QColor color:

import MyThemeModule 1.0 as Theme

Rectangle {
    color: Theme.color // привязка
}

QJSValue также может быть представлен как тип-синглтон, однако клиенты должны знать, что свойства такого типа-синглтона не могут быть привязаны к ним.

Для получения дополнительной информации о том, как реализовать и зарегистрировать новый тип-синглтон и как использовать существующий тип-синглтон, смотрите QML_SINGLETON.

Примечание. Значения перечисления для зарегистрированных типов в QML должны начинаться с заглавной буквы.

Финальные свойства

Свойства, объявленные финальными с использованием модификатора FINAL к Q_PROPERTY, не могут быть переопределены. Это означает, что любые свойства или функции с тем же именем, объявленные либо в QML, либо в C++ для производных типов, игнорируются механизмом QML. Вы должны объявлять свойства как FINAL, когда это возможно, чтобы избежать случайного переопределения. Переопределение свойства видно не только в производных классах, но и в коде QML, выполняющем контекст базового класса. Однако такой код QML обычно ожидает исходное свойство. Это частый источник ошибок.

Свойства, объявленные FINAL, также не могут быть переопределены функциями в QML или методами Q_INVOKABLE в C++.

Ревизии и версии типов

Многие функции регистрации типа требуют указания версии для зарегистрированного типа. Ревизии и версии типов позволяют новым свойствам или методам существовать в новой версии, оставаясь при этом совместимыми с предыдущими версиями.

Рассмотрим эти два файла QML:

// main.qml
import QtQuick 1.0

Item {
    id: root
    MyType {}
}
// MyType.qml
import MyTypes 1.0

CppType {
    value: root.x
}

где CppType сопоставляется с классом C++ CppType.

Если автор CppType добавляет свойство root в CppType в новой версии определения своего типа, root.x теперь разрешается в другое значение, поскольку root также является id компонента верхнего уровня. Автор может указать, что новое свойство root доступно в определенной дополнительной версии. Это позволяет добавлять новые свойства и функции к существующим типам без нарушения существующих программ.

Тег REVISION используется для обозначения свойства root как добавленного в ревизию типа № 1. Такие методы, как Q_INVOKABLE, сигналы и слоты, также могут быть помечены для ревизии с помощью макроса Q_REVISION(x):

class CppType : public BaseType
{
    Q_OBJECT
    Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION 1)
    QML_ELEMENT

signals:
    Q_REVISION(1) void rootChanged();
};

Указанные таким образом ревизии автоматически интерпретируются как минорные версии мажорной версии, указанной в файле проекта. В этом случае root доступен только при импорте MyTypes версии 1.1 или выше. Импорт MyTypes версии 1.0 остается неизменным.

По той же причине новые типы, представленные в более поздних версиях, должны быть помечены макросом QML_ADDED_IN_MINOR_VERSION.

Эта особенность языка позволяет вносить поведенческие изменения, не нарушая работу существующих приложений. Следовательно, авторы модуля QML должны всегда помнить о том, чтобы документировать, что изменилось между младшими версиями, а пользователи модуля QML должны убедиться, что их приложение по-прежнему работает правильно, прежде чем развертывать обновленную инструкцию импорта.

Ревизии базового класса, от которого зависит ваш тип, автоматически регистрируются при регистрации самого типа. Это полезно при наследовании от базовых классов, предоставленных другими авторами, например, при расширении классов из модуля Qt Quick.

Примечание. Механизм QML не поддерживает изменения свойств или сигналов сгруппированных и присоединенных объектов свойств.

Регистрация объектов расширения

При интеграции существующих классов и технологий в QML часто требуется настройка API, чтобы лучше вписаться в декларативную среду. Хотя наилучшие результаты обычно достигаются путем прямого изменения исходных классов, если это либо невозможно, либо осложняется какими-либо другими проблемами, объекты расширения допускают ограниченные возможности расширения без прямых изменений.

Объекты расширения добавляют дополнительные свойства к существующему типу. Объекты расширения могут добавлять только свойства, но не сигналы или методы. Определение расширенного типа позволяет программисту при регистрации класса указать дополнительный тип, известный как тип расширения. При использовании из QML свойства прозрачно объединяются с исходным целевым классом. Например:

QLineEdit {
    leftMargin: 20
}

Свойство leftMargin – это новое свойство, добавленное к существующему типу C++, QLineEdit, без изменения его исходного кода.

Макрос QML_EXTENDED(extended) предназначен для регистрации расширенных типов. Аргумент – это имя другого класса, который будет использоваться в качестве расширения.

Вы также можете использовать QML_EXTENDED_NAMESPACE(пространство имен) в качестве расширения типа для регистрации пространства имен и особенно перечислений, объявленных внутри.

Класс расширения – это обычный QObject с конструктором, принимающим указатель QObject. Однако создание класса расширения откладывается до тех пор, пока не будет осуществлен доступ к первому расширенному свойству. Создается класс расширения, и целевой объект передается в качестве родителя. При доступе к свойству оригинала вместо него используется соответствующее свойство объекта расширения.

Пример Extending QML - Extension Objects Example демонстрирует использование объектов расширения.

Регистрация внешних типов

Могут быть типы C++, которые нельзя изменить для хранения вышеупомянутых макросов. Это могут быть типы из сторонних библиотек или типы, которые должны выполнять какой-то контракт, который противоречит наличию этих макросов. Тем не менее, вы всё равно можете предоставить QML доступ к этим типам, используя макрос QML_FOREIGN. Для этого создайте отдельную структуру, полностью состоящую из регистрационных макросов, например:

// Содержит класс Immutable3rdParty
#include <3rdpartyheader.h>

struct Foreign
{
    Q_GADGET
    QML_FOREIGN(Immutable3rdParty)
    QML_NAMED_ELEMENT(Accessible3rdParty)
    QML_ADDED_IN_VERSION(2, 4)
    // QML_EXTENDED, QML_SINGLETON ...
};

Из этого кода вы получаете тип QML с методами и свойствами Immutable3rdParty, а также особенности QML (например, singleton, extended), указанные в Foreign.

Определение специфичных для QML типов и атрибутов

Предоставление прикрепленных свойств

В синтаксисе языка QML есть понятие прикрепленных свойств и прикрепленных обработчиков сигналов, которые являются дополнительными атрибутами, присоединяемыми к объекту. По сути, такие атрибуты реализуются и предоставляются присоединяемым типом, и эти атрибуты могут быть присоединены к объекту другого типа. Это контрастирует с обычными свойствами объекта, которые предоставляются самим объектным типом (или унаследованным типом объекта).

Например, в элементе ниже используются прикрепленные свойства и прикрепленные обработчики:

import QtQuick 2.0

Item {
    width: 100; height: 100

    focus: true
    Keys.enabled: false
    Keys.onReturnPressed: console.log("Return key was pressed")
}

Здесь объект Item может получить доступ и установить значения Keys.enabled и Keys.onReturnPressed. Это позволяет объекту Item получать доступ к этим дополнительным атрибутам как к расширению его собственных существующих атрибутов.

Шаги для реализации прикрепленных объектов

При рассмотрении приведенного выше примера есть несколько вовлеченных сторон:

  • Существует экземпляр анонимного присоединенного объектного типа с enabled и сигналом returnPressed, которые были присоединены к объекту Item, чтобы позволить ему получить доступ и установить эти атрибуты.
  • Объект Item является присоединяющим, к которому присоединен экземпляр присоединенного объектного типа.
  • Keys – это присоединяемый тип, который предоставляет присоединяющему объекту именованный квалификатор Keys, с помощью которого тот может получить доступ к атрибутам присоединенного объектного типа.

Когда механизм QML обрабатывает этот код, он создает один экземпляр присоединенного объектного типа и прикрепляет этот экземпляр к объекту Item, тем самым предоставляя ему доступ к атрибутам enable и returnPressed экземпляра.

Механизмы для предоставления присоединяемых объектов могут быть реализованы из C++ путем предоставления классов для присоединенного объектного типа и присоединяющего типа. Для присоединенного объектного типа предоставьте класс, производный от QObject, который определяет атрибуты, которые должны быть доступны для присоединяющих объектов. Для присоединяемого типа предоставьте класс, производный от QObject, который:

  • реализует статический метод qmlAttachedProperties() со следующей сигнатурой:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);

Этот метод должен возвращать экземпляр присоединенного объектного типа.

Движок QML вызывает этот метод для присоединения экземпляра присоединенного объектного типа к присоединяющему объекту, указанному параметром object. Обычно, хотя и не обязательно, для реализации этого метода возвращаемый экземпляр является родителем object, чтобы предотвратить утечку памяти.

Этот метод вызывается обработчиком не более одного раза для каждого экземпляра присоединяющего объекта, поскольку обработчик кэширует возвращаемый указатель экземпляра для последующего доступа к прикрепленному свойству. Следовательно, объект присоединения не может быть удален до тех пор, пока не будет уничтожен присоединяющий object.

  • объявляется как присоединяемый тип путем добавления макроса QML_ATTACHED(attached) в объявление класса. Аргумент – это имя присоединенного объектного типа.

Реализация прикрепленных объектов: пример

Например, возьмем тип Message, описанный в предыдущем примере:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
    Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
    QML_ELEMENT
public:
    // ...
};

Предположим, необходимо инициировать сигнал Message, когда оно публикуется на доске объявлений, а также отслеживать, когда срок действия сообщения истек. Поскольку не имеет смысла добавлять эти атрибуты непосредственно в Message, поскольку они более релевантны контексту доски сообщений, их можно реализовать как прикрепленные атрибуты к объекту Message, которые предоставляются через квалификатор MessageBoard. С точки зрения концепций, описанных ранее, вовлеченными сторонами здесь являются:

  • Экземпляр анонимного присоединенного объектного типа, который предоставляет сигнал published и свойство expired. Этот тип реализован в MessageBoardAttachedType ниже.
  • Объект Message, который будет присоединяющим.
  • Тип MessageBoard, который будет присоединяемым типом, используемым объектами Message для доступа к прикрепленным атрибутам.

Ниже приведен пример реализации. Во-первых, должен быть присоединенный объектный тип с необходимыми свойствами и сигналами, которые будут доступны присоединяющему:

class MessageBoardAttachedType : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged)
    QML_ANONYMOUS
public:
    MessageBoardAttachedType(QObject *parent);
    bool expired() const;
    void setExpired(bool expired);
signals:
    void published();
    void expiredChanged();
};

Затем присоединяемый тип, MessageBoard, должен объявить метод qmlAttachedProperties(), который возвращает экземпляр присоединенного объектного типа, как реализованный MessageBoardAttachedType. Кроме того, MessageBoard должен быть объявлен как присоединяемый тип с помощью макроса QML_ATTACHED():

class MessageBoard : public QObject
{
    Q_OBJECT
    QML_ATTACHED(MessageBoardAttachedType)
    QML_ELEMENT
public:
    static MessageBoardAttachedType *qmlAttachedProperties(QObject *object)
    {
        return new MessageBoardAttachedType(object);
    }
};

Теперь тип Message может получить доступ к свойствам и сигналам присоединенного объектного типа:

Message {
    author: "Amelie"
    creationDate: new Date()

    MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00")
    MessageBoard.onPublished: console.log("Message by", author, "has been
published!")
}

Кроме того, реализация C++ может получить доступ к экземпляру присоединенного объекта, который был присоединен к любому объекту, путем вызова функции qmlAttachedPropertiesObject().

Например:

Message *msg = someMessageInstance();
MessageBoardAttachedType *attached =
        qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg));

qDebug() << "Value of MessageBoard.expired:" << attached->expired();

Типы модификаторов свойств

Тип модификатора свойства – это особый вид объектного типа QML. Экземпляр типа модификатора свойства влияет на свойство (экземпляра объекта QML), к которому он применяется. Существует два разных типа модификаторов свойств:

  • перехватчики записи значений свойств;
  • источники значений свойств.

Перехватчик записи значения свойства можно использовать для фильтрации или изменения значений по мере их записи в свойства. В настоящее время единственным поддерживаемым перехватчиком записи значения свойства является тип Behavior, предоставляемый импортом QtQuick.

Источник значения свойства можно использовать для автоматического обновления значения свойства с течением времени. Клиенты могут определять свои собственные типы источников значений свойств. Примерами источников значений свойств являются различные типы анимации свойств, предоставляемые импортом QtQuick.

Экземпляры типа модификатора свойства можно создавать и применять к свойству объекта QML с помощью синтаксиса

<ТипМодификатора> on <имяСвойства>»,

как показано в следующем примере:

import QtQuick 2.0

Item {
    width: 400
    height: 50

    Rectangle {
        width: 50
        height: 50
        color: "red"

        NumberAnimation on x {
            from: 0
            to: 350
            loops: Animation.Infinite
            duration: 2000
        }
    }
}

Это обычно называют синтаксисом on.

Клиенты могут регистрировать свои собственные типы источников значений свойств, но в настоящее время не могут регистрировать перехватчики записи значений свойств.

Источники значений свойств

Источники значений свойств – это типы QML, которые могут автоматически обновлять значение свойства с течением времени, используя синтаксис

<ИсточникЗначенияСвойства> on <свойство>

Примерами источников значений свойств являются различные типы анимации свойств, предоставляемые импортом QtQuick.

Источник значения свойства может быть реализован на C++ путем создания подкласса QQmlPropertyValueSource и предоставления реализации, которая со временем записывает в свойство разные значения. Когда источник значения свойства применяется в QML к свойству с использованием синтаксиса <ИсточникЗначенияСвойства> on <свойство>, он ​​получает ссылку на это свойство, чтобы можно было обновить его значение.

Например, предположим, что имеется класс RandomNumberGenerator, который должен быть доступен в качестве источника значения свойства, чтобы при применении к свойству QML он обновлял значение свойства на другое случайное число каждые 500 миллисекунд. Кроме того, этому генератору случайных чисел может быть предоставлено значение maxValue. Этот класс может быть реализован следующим образом:

class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource
{
    Q_OBJECT
    Q_INTERFACES(QQmlPropertyValueSource)
    Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged);
    QML_ELEMENT
public:
    RandomNumberGenerator(QObject *parent)
        : QObject(parent), m_maxValue(100)
    {
        QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty()));
        m_timer.start(500);
    }

    int maxValue() const;
    void setMaxValue(int maxValue);

    virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; }

signals:
    void maxValueChanged();

private slots:
    void updateProperty() {
        m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue));
    }

private:
    QQmlProperty m_targetProperty;
    QTimer m_timer;
    int m_maxValue;
};

Когда механизм QML обнаруживает использование RandomNumberGenerator в качестве источника значения свойства, он вызывает RandomNumberGenerator::setTarget() для предоставления типа со свойством, к которому был применен источник значения. Когда внутренний таймер в RandomNumberGenerator срабатывает каждые 500 миллисекунд, он записывает новое числовое значение в указанное свойство.

После регистрации класса RandomNumberGenerator в системе типов QML его можно использовать из QML в качестве источника значения свойства. В примере ниже он используется для изменения ширины прямоугольника каждые 500 миллисекунд:

import QtQuick 2.0

Item {
    width: 300; height: 300

    Rectangle {
        RandomNumberGenerator on width { maxValue: 300 }

        height: 100
        color: "red"
    }
}

Во всех других отношениях источники значений свойств – это обычные типы QML, которые могут иметь свойства, методы сигналов и т.д., но с дополнительной возможностью, заключающейся в том, что их можно использовать для изменения значений свойств с использованием синтаксиса <ИсточникЗначенияСвойства> on <свойство>.

Когда объект источника значения свойства присваивается свойству, QML сначала пытается присвоить его обычным образом, как если бы это был обычный тип QML. Только если это присваивание не выполняется, движок вызывает метод setTarget(). Это позволяет также использовать этот тип в других контекстах, а не только в качестве источника значения.

Указание свойств по умолчанию и родительских свойств для объектных типов QML

Любой тип, производный от QObject, который зарегистрирован как объектный тип QML с возможностью создания экземпляра, может дополнительно указать свойство по умолчанию для типа. Свойство по умолчанию – это свойство, которому автоматически присваиваются дочерние объекты объекта, если они не присвоены какому-либо конкретному свойству.

Свойство по умолчанию можно установить, вызвав макрос Q_CLASSINFO() для класса с определенным значением "DefaultProperty". Например, класс MessageBoard ниже указывает свое свойство messages как свойство по умолчанию для класса:

class MessageBoard : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
    Q_CLASSINFO("DefaultProperty", "messages")
    QML_ELEMENT
public:
    QQmlListProperty<Message> messages();

private:
    QList<Message *> m_messages;
};

Это позволяет дочерним элементам объекта MessageBoard автоматически присваиваться его свойству messages, если они не присвоены определенному свойству. Например:

MessageBoard {
    Message { author: "Naomi" }
    Message { author: "Clancy" }
}

Если бы messages не были установлены в качестве свойства по умолчанию, то любые объекты Message вместо этого должны были бы быть явно назначены свойству messages, как показано ниже:

MessageBoard {
    messages: [
        Message { author: "Naomi" },
        Message { author: "Clancy" }
    ]
}

Между прочим, свойство Item::data является его свойством по умолчанию. Любые объекты Item, добавленные к этому свойству data, также добавляются в список Item::children, поэтому использование свойства по умолчанию позволяет объявлять для элемента визуальные дочерние элементы без явного присваивания их свойству children.

Кроме того, вы можете объявить "ParentProperty" Q_CLASSINFO(), чтобы сообщить механизму QML, какое свойство должно обозначать родительский объект в иерархии QML. Например, тип Message может быть объявлен следующим образом:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QObject* board READ board BINDABLE boardBindable)
    Q_PROPERTY(QString author READ author BINDABLE authorBindable)
    Q_CLASSINFO("ParentProperty", "board")
    QML_ELEMENT

public:
    Message(QObject *parent = nullptr) : QObject(parent) { m_board = parent; }

    QObject *board() const { return m_board.value(); }
    QBindable<QObject *> boardBindable() { return QBindable<QObject *>(&m_board); }

    QString author() const { return m_author.value(); }
    QBindable<QString> authorBindable() { return QBindable<QString>(&m_author); }

private:
    QProperty<QObject *> m_board;
    QProperty<QString> m_author;
};

Определение родительского свойства позволяет qmllint и другим инструментам лучше понять назначение вашего кода и позволяет избежать ложных срабатываний предупреждений при доступе к некоторым свойствам.

Определение визуальных элементов с помощью модуля Qt Quick

При построении пользовательских интерфейсов с помощью модуля Qt Quick все объекты QML, которые должны визуализироваться, должны быть производными от типа Item, так как это базовый тип для всех визуальных объектов в Qt Quick. Этот тип Item реализован классом C++ QQuickItem, который предоставляется модулем Qt Quick. Следовательно, необходимо наследоваться от этого класса, когда требуется реализовать на C++ визуальный тип, который можно интегрировать в пользовательский интерфейс на основе QML.

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

Получение уведомлений об инициализации объектов

Для некоторых пользовательских объектных типов QML может быть полезно отложить инициализацию определенных данных до тех пор, пока объект не будет создан и все его свойства не будут установлены. Например, это может иметь место, если инициализация является дорогостоящей или если инициализация не должна выполняться до тех пор, пока не будут инициализированы все значения свойств.

Модуль Qt QML для этих целей предоставляет для наследования класс QQmlParserStatus. Он определяет ряд виртуальных методов, которые вызываются на различных этапах создания экземпляра компонента. Чтобы получать эти уведомления, класс C++ должен наследовать QQmlParserStatus, а также уведомлять метасистему Qt с помощью макроса Q_INTERFACES().

Например:

class MyQmlType : public QObject, public QQmlParserStatus
{
    Q_OBJECT
    Q_INTERFACES(QQmlParserStatus)
    QML_ELEMENT
public:
    virtual void componentComplete()
    {
        // Выполните здесь какую-либо инициализацию,
        // теперь, когда объект полностью создан
    }
};

Теги

C++ / CppGUI / Графический интерфейс пользователяQMLQtQtQuickПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.