На Хабре и в Сети достаточно много статей на тему QML, но все они оставляют за кадром некоторые моменты. Сегодня я попытаюсь приподнять занавес над некоторыми очевидными моментами для тех, кто имел дело со связкой QML и C++, и не таких очевидных для тех, кто только начинает вникать в нюансы этой замечательной технологии.
Итак. Допустим, у нас есть интерфейс приложения на QML и C++ класс с логикой работы. Как же нам собрать все это в единое целое? Начнем с нашего C++ класса, самое первое что нам нужно сделать для того что бы он был доступен из QML – это унаследовать его от QObject либо любого другого наследника этого самого QObject.
Теперь сделаем доступным из QML какое-нибудь свойство, для этого существует макрос Q_PROPERTY.
Здесь someProperty собственно само наше свойство, getSomeProperty – метод для чтения, setSomeProperty – метод для записи. Если мы собираемся менять наше свойство из C++ то нам необходимо уведомлять об этом интерфейс на QML с помощью сигнала somePropertyChanged. Теперь должны зарегистрировать наш класс в QML, для этого нам нужно вызвать в конструкторе QmlApplicationViewer, который создается QtCreator автоматически при создании нового QtQuick проекта, добавить вызов шаблонной функции qmlRegisterTypes.
Здесь TestClass – наш класс, ModuleName – имя импортируемого QML модуля, TypeName – имя типа объектов в QML. Теперь в QML файле импортируем наш класс, и создаем экземпляр.
Подробно нас интересуют следующие моменты:
1)
2)
Собственно создание объекта нашего типа и запись свойства.
3)
Компилируем, запускаем.

Для того что бы иметь возможность вызывать из QML C++ методы их необходимо определять как слоты либо при помощи макроса Q_INVOKABLE.
Модифицируем QML файл.
Компилируем, запускаем, нажимаем на квадраты и по отладочному выводу видим что все работает, вот только изменения нашего свойства никак не отражаются на интерфейсе. Но мы это предвидели и при объявлении свойства указали NOTIFY somePropertyChanged, модифицируем наш метод и слот так что бы при изменении свойства испускался сигнал somePropertyChanged.
Снова компилируем, запускаем и наслаждаемся – оно реагирует.
А как же нам быть если сами мы хотим обработать сигнал, испускаемый C++ объектом, из QML кода? Все просто. Добавляем в наш класс сигнал и генерируем его в угодный нам момент.
Для того что бы поймать этот сигнал в QML нашему объекту необходим обработчик события onSomeSignal, имена событий в QML получаются путем преобразования первой буквы сигнала к верхнему регистру и добавления префикса on.
Вот так выглядит создание объекта с обработчиком событий в QML файле.
Вот собственно и все что я хотел рассказать. Спасибо за внимание.
Итак. Допустим, у нас есть интерфейс приложения на 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)
– так мы говорим QML движку в каком модуле мы будем искать наш тип.import ModuleName 1.0
2)
TypeName{ id: myObj someProperty: 10 }
Собственно создание объекта нашего типа и запись свойства.
3)
– Чтение нашего свойства.text: "My property is: " + 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 файле.
Вот собственно и все что я хотел рассказать. Спасибо за внимание.