|
Общий QML
Оглавление - Основы
- Правила QML
- Свойства и соединения
- Динамическое создание объектов
- JavaScript
- Перевод приложения
- Взаимодействие между QML и C++
- Основы
- Нерегистрируемые в QML объекты
- Регистрируемые в QML объекты
- Реализация специфичных для QML типов и аттрибутов
- Прикреплённые объекты и аннотации
- Модификаторы свойств
- Определение свойства по-умолчанию для QML-объектов в С++
- Получение уведомлений об инициализации объекта
- Рекомедации
- Общие рекомендации
- Оформление кода
- Масштабируемость
- Оптимизации
- Полезности
- Общие
- Двунаправленное соединение
- Скрытые свойства в подключаемом элементе
- Неизменяемое снаружи свойство в подключаемом элементе, которое можно изменять внутри
- Реализация класса асинхронной загрузки изображений
- Работа с компонентами
- StackView(controls1)
- Специфика платформ(пока нет)
- Android
- iOS
- Linux
- Windows
- Список ссылок и литературы
Взаимодействие между QML и C++ http://doc.qt.io/qt-5/qtqml-cp... topic.html
Основы
QML использует метаобъектную систему Qt. Поэтому объект С++, который нужно применять в QML, должен всегда прямо или косвенно наследоваться от QObject. При этом все свойства(объявляемые макросом Q_PROPERTY), слоты и сигналы данного объекта будут доступны в QML. Обычные функции объекта делаются доступными в QML с помощью макроса Q_INVOKABKE.
Объекты взаимодейтствия между Qt и QML в основном разделяются на два типа:- Нерегистрируемые
- Регистрируемые
Нерегистрируемые в QML объекты
Нерегистрируемые объекты представляют собой классы, унаследованные от QObject или любой другой объект(int, bool, QUrl и т.д.), который можно преобразовать в базовый тип QML. Они вносятся в контекст QML под определённым именем, через которое к ним и получают доступ.
Пример
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| class ApplicationData : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QDateTime getCurrentDateTime() const {
return QDateTime::currentDateTime();
}
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view;
ApplicationData data;
view.rootContext()->setContextProperty("applicationData", &data);
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
return app.exec();
} |
|
| JavaScript | 1
2
3
4
| // MyItem.qml
import QtQuick 2.0
Text { text: applicationData.getCurrentDateTime() } |
|
В таком случае возникает проблема, если надо каким-то образом в QML привязать слот в подсоединённом объекте с какими-либо свойствами или сигналами других объектов. В этом случае нужно применять QML класс Binding.
Пример
| JavaScript | 1
2
3
4
5
6
7
8
9
| Text {
text: applicationData.getCurrentDateTime() // applicationData - подсоединённые объект
Binding {
target: applicationData
property: "someProperty"
value: "someValue" // значение, которое вешается на свойство someValue объекта applicationData
}
} |
|
Регистрируемые в QML объекты
Любой унаследованный от QObject класс может быть зарегистрирован как QML-объект.
С++: QML разделяет классы регистрируемых объектов на виды:- Закрытые для QML. Регистрируются помощью qmlRegisterType без параметров. Данный тип нельзя создавать в QML и к нему нельзя обращаться. К примеру, если зарегистрировать подобный класс как базовый, а класс-наследник как обычный, то в QML нельзя будет получить доступ из класса наследника к свойствам, сигналам и другой мета-информации, унаследованной от подобного базового класса.
- Интерфейсы. Регистрируются с помощью qmlRegisterInterface. Данный тип нельзя создавать, но можно к нему обращаться.
- Не создаваемые QML. Регистрируются с помощью qmlRegisterUncreatableType. Данный тип нельзя создавать в QML, но к нему можно обращаться. Это полезно, когда класс не должен создаваться в QML, но в тоже время его перечисляемые типы(enum) или свойства должны быть доступны из QML.
- Синглтоны. Регистрируются с помощью qmlRegisterSingletonType. Данный тип в единственном экземпляре автоматически создаётся QML(и уничтожается по окончанию работы) и к нему спокойно можно обращаться по его имени. Может быть не только QObject, но и QJSValue.
- Обычные объекты QML. Регистрируются с помощью qmlRegisterType с параметрами. Объекты данного типа можно спокойно создавать в QML и к ним можно обращаться.
- Расширение объектов. Регистрируются с помощью qmlRegisterExtendedType. Расширяют обычные объекты, предоставляя им дополнительные свойства.
Пример обычного объекта QML
C++ часть:
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| 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)
public:
Q_INVOKABLE void do_some_operation();
//...
public slots:
void do_some_other_operation();
//...
signals:
void someSignal();
// ...
};
#inclide <QtQml>
qmlRegisterType<Message>("com.mycompany.messaging", 1, 0, "Message"); |
|
QML часть:
| JavaScript | 1
2
3
4
5
6
7
8
9
10
| import com.mycompany.messaging 1.0
Message {
author: "Amelie"
creationDate: new Date()
onSomeSignal: do_some_operation()
Component.onCompleted: do_some_other_operation()
} |
|
Реализация специфичных для QML типов и аттрибутов
Прикреплённые объекты и аннотации
Создание прикреплённых типов и свойств.
Для реализации подобного рода функционала нужно в унаследованном от QObject классе определить функцию static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object), которая должна возвратить прикреплённый объект. А сам класс обйявить с помощью QML_DECLARE_TYPEINFO как класс, имеющий прикреплённые свойства QML_HAS_ATTACHED_PROPERTIES.
Пример
Обычный класс QML, приведённые ранее:
| C++ (Qt) | 1
2
3
4
5
6
7
8
| 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)
public:
// ...
}; |
|
Прикреплённый класс:
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
| class MessageBoardAttachedType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool expired READ expired WRITE expired NOTIFY expiredChanged)
public:
MessageBoardAttachedType(QObject *parent);
bool expired() const;
void setExpired(bool expired);
signals:
void published();
void expiredChanged();
}; |
|
Обёртка прикреплённого класса:
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
| class MessageBoard : public QObject
{
Q_OBJECT
public:
static MessageBoard *qmlAttachedProperties(QObject *object)
{
return new MessageBoardAttachedType(object);
}
};
QML_DECLARE_TYPEINFO(MessageBoard, QML_HAS_ATTACHED_PROPERTIES) |
|
QML: | JavaScript | 1
2
3
4
5
6
7
8
| 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!")
} |
|
Модификаторы свойств
Имеют в QML вид <ModifierType> on <propertyName>.
Есть два разных типа модификаторов свойств:- Перехватчики записи свойств. Используются для модификации или фильтрования значений, когда они записываются в свойства. Ими, к примеру, является Behavior.
- Источники значений свойств. Используются для обновления свойств, например, для автоматического обновления свойств по времени. Ими, к примеру, являются различные типы анимаций свойств(property animation types).
Источники значений свойств реализуются в С++ посредством унаследования класса QQmlPropertyValueSource и реализации функционала, который записывает в свойство разные значения по времени.
Пример
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource
{
Q_OBJECT
Q_INTERFACES(QQmlPropertyValueSource)
Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged);
public:
RandomNumberGenerator(QObject *parent)
: QObject(parent), m_maxValue(100)
{
qsrand(QDateTime::currentDateTime().toTime_t());
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(qrand() % m_maxValue);
}
private:
QQmlProperty m_targetProperty;
QTimer m_timer;
int m_maxValue;
}; |
|
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
| import QtQuick 2.0
Item {
width: 300; height: 300
Rectangle {
RandomNumberGenerator on width { maxValue: 300 }
height: 100
color: "red"
}
} |
|
Определение свойства по-умолчанию для QML-объектов в С++
С++ объект, который зарегистрирован как объект QML, может обладать свойством по-умолчанию(дефолтное свойство). Свойство по-умолчанию - это свойство к которому причисляются все подэлементы данного объекта, если их не причислить к другим свойствам. Свойство по-умолчанию может быть установлено с помощью установки в Q_CLASSINFO по ключу "DefaultProperty" необходимого свойства.
Пример
Ниже класс MessageBoard определяет его свойство messages(сообщения) как свойство по-умолчанию:
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
| class MessageBoard : public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Message> messages READ messages) // есть мнение, что здесь ошибка из-за copy-paste
// и должен быть указан ещё метод записи - WRITE
Q_CLASSINFO("DefaultProperty", "messages")
public:
QQmlListProperty<Message> messages() const;
private:
QList<Message *> messages;
}; |
|
| JavaScript | 1
2
3
4
| MessageBoard {
Message { author: "Naomi" } // Класс Message заносится в свойство по-умолчанию(в данном случае в свойство messages).
Message { author: "Clancy" }
} |
|
Получение уведомлений об инициализации объекта
Для некоторых QML объектов необходимо, чтобы некоторые операции происходили уже после их создания и инициализации. Специально для этих целей существует класс QQmlParserStatus. Для получения уведомлений С++ класс должен унаследовать QQmlParserStatus и проинформировать мета-систему Qt с помощью макроса Q_INTERFACES.
Пример
| JavaScript | 1
2
3
4
5
6
7
8
9
10
| class MyQmlType : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
public:
virtual void componentComplete()
{
// Провести необходимые операции, когда объект полностью создан
}
}; |
|
|