Интеграция C++ и QML
Нативные приложения для ОС Аврора пишутся с помощью Qt 5.6. При этом интерфейс приложений создаётся средствами QML, а работа с данными может быть организована с помощью C++. Таким образом, нередко необходимо передавать данные между C++ и QML-частями приложения. Для этого можно зарегистрировать QML-тип на основе C++ класса.
Расширение C++-класса
Для создания C++-класса, доступного из QML нужно:
- Унаследовать его от
QObject
либо любого другого наследникаQObject
. Например, визуальные элементы наследуются отQQuickItem
. - Добавить макрос
Q_OBJECT
в описание класса. - Зарегистрировать свойства, доступные из QML, с помощью макроса
Q_PROPERTY
. - Зарегистрировать методы, доступные из QML, с помощью макроса
Q_INVOKABLE
. Или следует поместить метод в секциюpublic slots
.
Пример C++-класса, не включённого в систему объектов Qt:
#ifndef VALUESTORAGE_H
#define VALUESTORAGE_H
class ValueStorage
{
public:
explicit ValueStorage(QObject *parent = nullptr);
int value() const;
void setValue(int value);
void resetValue();
private:
int m_value;
};
Пример расширения этого класса с доступом к свойству и методам из QML:
#ifndef VALUESTORAGE_H
#define VALUESTORAGE_H
#include <QObject>
class ValueStorage : public QObject
{
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit ValueStorage(QObject *parent = nullptr);
int value() const;
Q_INVOKABLE void resetValue();
public slots:
void setValue(int value);
signals:
void valueChanged();
private:
int m_value;
};
В примере экспортируется свойство value
и методы resetValue
и valueChanged
.
Пример основан на учебных материалах о разработке под ОС Аврора.
Подробнее о макросах можно узнать в документации Qt.
Реализация объявленного класса в cpp-файле:
#include "valuestorage.h"
#include <QDebug>
ValueStorage::ValueStorage(QObject *parent) :
QObject(parent),
m_value(0)
{ }
int ValueStorage::value() const
{
return m_value;
}
void ValueStorage::setValue(int value)
{
if (m_value == value)
return;
m_value = value;
emit valueChanged();
}
void ValueStorage::resetValue()
{
setValue(0);
}
Регистрация QML-типа
После того как класс расширен, его можно регистрировать как
QML-тип.
Для этого в main.cpp
следует выполнить следующие действия:
- Подключить класс посредством директивы
#include
:#include "valuestorage.h"
. - Зарегистрировать QML-тип в функции
main
с помощью методаqmlRegisterType
:qmlRegisterType<ValueStorage>("ru.auroraos.Counter", 1, 0, "ValueStorage");
. При этом следует придумать имя QML-модуля, в котором будет находиться новый тип (ru.auroraos.Counter
в примере), его версию и имя QML-типа (ValueStorage
). Имя QML-модуля должно соответствовать требованиям валидатора. Имя QML-компонента может совпадать с именем C++-класса. Оно не должно совпадать с именами компонентов из Qt Quick и QML-плагинов, чтобы не было конфликта имён.
Полный пример кода:
#include <auroraapp.h>
#include <QtQuick>
#include "valuestorage.h"
int main(int argc, char *argv[])
{
QScopedPointer<QGuiApplication> application(Aurora::Application::application(argc, argv));
application->setOrganizationName(QStringLiteral("ru.auroraos"));
application->setApplicationName(QStringLiteral("Counter"));
qmlRegisterType<ValueStorage>("ru.auroraos.Counter", 1, 0, "ValueStorage");
QScopedPointer<QQuickView> view(Aurora::Application::createView());
view->setSource(Aurora::Application::pathTo(QStringLiteral("qml/Counter.qml")));
view->show();
return application->exec();
}
Использование нового типа в QML
Зарегистрированный QML-тип можно импортировать в QML по имени модуля:
import ru.auroraos.Counter 1.0
Затем можно создать QML-объект:
ValueStorage {
id: valueStorage
}
У объекта можно получить значение свойства value
:
Text {
text: "Value: %1".arg(valueStorage.value)
...
}
Или изменить его:
onClicked: valueStorage.value++
Объекту можно добавить обработчик сигнала:
ValueStorage {
id: valueStorage
onValueChanged: console.log(value)
}
Можно вызвать метод:
onClicked: valueStorage.resetValue()
Полный пример кода:
import QtQuick 2.0
import ru.auroraos.Counter 1.0
Item {
id: root
objectName: "Counter"
property real fontSize: width / 16
ValueStorage {
id: valueStorage
objectName: "valueStorage"
onValueChanged: console.log(value)
}
Column {
objectName: "layout"
width: parent.width
Text {
id: valueText
objectName: "valueText"
text: "Value: %1".arg(valueStorage.value)
height: root.height / 3
verticalAlignment: Text.AlignVCenter
font.pixelSize: fontSize
anchors.horizontalCenter: parent.horizontalCenter
}
Row {
objectName: "buttonsRow"
height: root.height - valueText.height
MouseArea {
width: root.width / 2
height: parent.height
Text {
text: "Increment"
anchors.centerIn: parent
font.pixelSize: fontSize
}
onClicked: valueStorage.value++
}
MouseArea {
width: root.width / 2
height: parent.height
Text {
text: "Reset"
anchors.centerIn: parent
font.pixelSize: fontSize
}
onClicked: valueStorage.resetValue()
}
}
}
}