Pull to refresh

C++ объекты и QML, все по полочкам

Reading time 4 min
Views 45K
На Хабре и в Сети достаточно много статей на тему 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)
text: "My property is: " + myObj.someProperty
– Чтение нашего свойства.

Компилируем, запускаем.
image

Для того что бы иметь возможность вызывать из 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 файле.
Вот собственно и все что я хотел рассказать. Спасибо за внимание.
Tags:
Hubs:
+2
Comments 12
Comments Comments 12

Articles