Определение типов QML из C++
При расширении QML кодом на C++, класс C++ может быть зарегистрирован в системе типов QML, чтобы этот класс можно было использовать как тип данных в коде QML. В то время как свойства, методы и сигналы любого класса, производного от QObject, доступны из QML, как обсуждается в Размещении атрибутов типов C++ в QML, такой класс нельзя использовать как тип данных из QML, пока он не будет зарегистрирован в системе типов. Кроме того, регистрация может обеспечить другие возможности, такие как использование класса в качестве экземплярируемого типа QML-объекта из QML или включение одиночного экземпляра класса для импорта и использования из QML.
Кроме того, модуль Qt QML предоставляет механизмы для реализации функций, специфичных для QML, таких как прикреплённые свойства и свойства по умолчанию, на C++.
(Обратите внимание, что ряд важных концепций, описанных в этом документе, продемонстрированы в учебном пособии «Написание расширений 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>
Кроме того, объявления вашего класса должны находиться в заголовочных файлах, доступных через путь включения вашего проекта. Объявления используются для генерации кода регистрации во время компиляции, а код регистрации должен включать заголовочные файлы, содержащие объявления.
Регистрация экземплярируемого типа объекта
Любой класс C++, производный от QObject, может быть зарегистрирован как определение типа 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 и creationDate.
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 через одиночный экземпляр и не должен быть экземпляризуем из QML
Модуль Qt QML предоставляет несколько макросов для регистрации неэкземплярируемых типов:
- QML_ANONYMOUS регистрирует тип C++, который не является экземпляризуемым и не может быть упомянут из QML. Это позволяет движку принудительно преобразовывать любые наследуемые типы, которые могут быть экземпляризованы из QML.
- QML_INTERFACE регистрирует существующий тип интерфейса Qt. Тип не может быть экземпляризован из QML, и вы не можете объявлять свойства QML с ним. Однако использование свойств C++ этого типа из QML будет выполнять ожидаемые преобразования интерфейса.
- QML_UNCREATABLE(reason) в сочетании с 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 есть свойство QColor «color»:
import MyThemeModule 1.0 as Theme
Rectangle {
color: Theme.color // binding.
} QJSValue также можно экспонировать как тип-синглтон, однако клиенты должны знать, что свойства такого типа-синглтона не могут быть связаны.
См. QML_SINGLETON для получения дополнительной информации о том, как реализовать и зарегистрировать новый тип-синглтон и как использовать существующий тип-синглтон.
Примечание: Значения перечислений для зарегистрированных типов в QML должны начинаться с заглавной буквы.
Финальные свойства
Свойства, объявленные final с помощью модификатора 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(namespace) для регистрации пространства имён, а особенно перечислений, объявленных в нём, как расширение типа.
Класс расширения — это обычный объект QObject с конструктором, принимающим указатель на объект QObject. Однако создание класса расширения откладывается до первого доступа к свойству расширения. Класс расширения создаётся, и целевой объект передаётся в качестве родителя. Когда свойство в оригинале обращается, вместо него используется соответствующее свойство в объекте расширения.
Пример объектов расширения демонстрирует использование объектов расширения.
Регистрация внешних типов
Могут быть типы C++, которые нельзя изменить для хранения вышеупомянутых макросов. Это могут быть типы из библиотек сторонних разработчиков или типы, которые должны выполнять какой-либо контракт, противоречащий наличию этих макросов. Тем не менее, вы по-прежнему можете экспонировать эти типы в QML, используя макрос QML_FOREIGN. Для этого создайте отдельную структуру, состоящую только из макросов регистрации, как показано ниже:
// Contains class 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 существует понятие присоединённых свойств и присоединённых обработчиков сигналов, которые являются дополнительными атрибутами, присоединёнными к объекту. По существу, такие атрибуты реализуются и предоставляются типом присоединения, и эти атрибуты могут быть присоединены к объекту другого типа. Это отличается от обычных свойств объектов, которые предоставляются самим типом объекта (или наследуемым типом объекта).
Например, объект Item ниже использует присоединённые свойства и обработчики:
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, тем самым предоставляя ему доступ к атрибутам enabled и 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 с помощью синтаксиса "<ТипМодификатора> на <имяСвойства>", как показано в следующем примере:
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, которые могут автоматически обновлять значение свойства со временем, используя синтаксис <PropertyValueSource> on <property>. Например, различные типы анимации свойств, предоставляемые модулем QtQuick, являются примерами источников значений свойств.
Источник значений свойства может быть реализован на C++ путём наследования от QQmlPropertyValueSource и предоставлением реализации, которая со временем записывает разные значения в свойство. Когда источник значения свойства применяется к свойству с помощью синтаксиса <PropertyValueSource> on <property> в QML, движок предоставляет ему ссылку на это свойство, чтобы значение свойства можно было обновить.
Например, предположим, что существует класс RandomNumberGenerator, который должен быть доступен в качестве источника значения свойства, так что при применении его к свойству QML он будет обновлять значение свойства на случайное число каждые 500 миллисекунд. Кроме того, этому генератору случайных чисел можно предоставить максимальное значение. Этот класс можно реализовать следующим образом:
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 в качестве источника значений свойств. Ниже показано его использование для изменения ширины Rectangle каждые 500 миллисекунд:
import QtQuick 2.0
Item {
width: 300; height: 300
Rectangle {
RandomNumberGenerator on width { maxValue: 300 }
height: 100
color: "red"
}
} Во всех остальных отношениях, источники значений свойств — это обычные типы QML, которые могут иметь свойства, сигналы, методы и так далее, но с добавлением возможности изменять значения свойств с помощью синтаксиса <PropertyValueSource> on <property>.
Когда объект источника значения свойства назначается свойству, 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. Кроме того, учебник Writing QML Extensions with 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()
{
// Perform some initialization here now that the object is fully created
}
};
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtqml-cppintegration-definetypes.html