На Хабре и в Сети достаточно много статей на тему QML, но все они оставляют за кадром некоторые моменты. Сегодня я попытаюсь приподнять занавес над некоторыми очевидными моментами для тех, кто имел дело со связкой QML и C++, и не таких очевидных для тех, кто только начинает вникать в нюансы этой замечательной технологии.
Итак. Допустим, у нас есть интерфейс приложения на QML и C++ класс с логикой работы. Как же нам собрать все это в единое целое? Начнем с нашего C++ класса, самое первое что нам нужно сделать для того что бы он был доступен из QML – это унаследовать его от QObject либо любого другого наследника этого самого QObject.
class TestClass : public QObject
{
Q_OBJECT
public:
explicit TestClass(QObject *parent = 0);
signals:
public slots:
};
Теперь сделаем доступным из QML какое-нибудь свойство, для этого существует макрос Q_PROPERTY.
class TestClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int someProperty READ getSomeProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
public:
explicit TestClass(QObject *parent = 0);
int getSomeProperty()const;
void setSomeProperty(const int &);
private:
int someProperty;
signals:
void somePropertyChanged();
public slots:
};
int TestClass::getSomeProperty()const
{
qDebug() << "I'm getter";
return someProperty;
}
void TestClass::setSomeProperty(const int &i)
{
qDebug() << "I'm setter";
someProperty = i;
}
Здесь someProperty собственно само наше свойство, getSomeProperty – метод для чтения, setSomeProperty – метод для записи. Если мы собираемся менять наше свойство из C++ то нам необходимо уведомлять об этом интерфейс на QML с помощью сигнала somePropertyChanged. Теперь должны зарегистрировать наш класс в QML, для этого нам нужно вызвать в конструкторе QmlApplicationViewer, который создается QtCreator автоматически при создании нового QtQuick проекта, добавить вызов шаблонной функции qmlRegisterTypes.
qmlRegisterType<TestClass>("ModuleName", 1, 0, "TypeName");
Здесь TestClass – наш класс, ModuleName – имя импортируемого QML модуля, TypeName – имя типа объектов в QML. Теперь в QML файле импортируем наш класс, и создаем экземпляр.
import QtQuick 1.0
import ModuleName 1.0
Rectangle {
width: 360
height: 360
TypeName{
id: myObj
someProperty: 10
}
Text {
text: "My property is: " + myObj.someProperty
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}
Подробно нас интересуют следующие моменты:
1)import ModuleName 1.0
– так мы говорим QML движку в каком модуле мы будем искать наш тип.
2) TypeName{
id: myObj
someProperty: 10
}
Собственно создание объекта нашего типа и запись свойства.
3) myObj.someProperty
– Чтение нашего свойства.
Компилируем, запускаем.
Для того что бы иметь возможность вызывать из QML C++ методы их необходимо определять как слоты либо при помощи макроса Q_INVOKABLE.
class TestClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int someProperty READ getSomeProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
public:
explicit TestClass(QObject *parent = 0);
int getSomeProperty()const;
void setSomeProperty(const int &);
Q_INVOKABLE
void myMethod();
private:
int someProperty;
signals:
void somePropertyChanged();
public slots:
void mySlot();
};
void TestClass::myMethod()
{
qDebug() << "I am Method";
someProperty++;
}
void TestClass::mySlot()
{
qDebug() << "I am SLOT";
someProperty--;
}
Модифицируем QML файл.
import QtQuick 1.0
import ModuleName 1.0
Rectangle {
width: 360
height: 360
TypeName{
id: myObj
someProperty: 10
}
Text {
text: "My property is: " + myObj.someProperty
anchors.centerIn: parent
}
Rectangle{
width: 20
height: 20
color: "red"
MouseArea {
anchors.fill: parent
onClicked: {
myObj.mySlot();
}
}
}
Rectangle{
anchors.right: parent.right
width: 20
height: 20
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
myObj.myMethod();
}
}
}
}
Компилируем, запускаем, нажимаем на квадраты и по отладочному выводу видим что все работает, вот только изменения нашего свойства никак не отражаются на интерфейсе. Но мы это предвидели и при объявлении свойства указали NOTIFY somePropertyChanged, модифицируем наш метод и слот так что бы при изменении свойства испускался сигнал somePropertyChanged.
void TestClass::myMethod()
{
qDebug() << "I am Method";
someProperty++;
emit somePropertyChanged();
}
void TestClass::mySlot()
{
qDebug() << "I am SLOT";
someProperty--;
emit somePropertyChanged();
}
Снова компилируем, запускаем и наслаждаемся – оно реагирует.
А как же нам быть если сами мы хотим обработать сигнал, испускаемый C++ объектом, из QML кода? Все просто. Добавляем в наш класс сигнал и генерируем его в угодный нам момент.
void TestClass::mySlot()
{
qDebug() << "I am SLOT";
someProperty--;
emit somePropertyChanged();
if(someProperty < 0) emit someSignal();
}
Для того что бы поймать этот сигнал в QML нашему объекту необходим обработчик события onSomeSignal, имена событий в QML получаются путем преобразования первой буквы сигнала к верхнему регистру и добавления префикса on.
TypeName{
id: myObj
someProperty: 10
onSomeSignal: {
Qt.quit();
}
}
Вот так выглядит создание объекта с обработчиком событий в QML файле.
Вот собственно и все что я хотел рассказать. Спасибо за внимание.
Автор: echoby