Spec-Zone.ru › Qt 5.6

Взаимодействие с объектами QML из C++

Все типы объектов QML являются производными от QObject, независимо от того, реализованы ли они в движке или определены сторонними источниками. Это означает, что движок QML может использовать Qt Механизм метаобъектов для динамического создания любого типа объекта QML и проверки созданных объектов.

Это полезно для создания объектов QML из кода C++, как для отображения визуализируемого объекта QML, так и для интеграции данных невизуальных объектов QML в приложение C++. После создания объекта QML его можно проверить из C++, чтобы считывать и записывать свойства, вызывать методы и получать уведомления о сигналах.

Загрузка объектов QML из C++

Документ QML можно загрузить с помощью QQmlComponent или QQuickView. QQmlComponent загружает документ QML как объект C++, который затем можно изменить из кода C++. QQuickView также делает это, но поскольку QQuickView является производным классом от QWindow, загруженный объект также будет отображен; QQuickView обычно используется для интеграции отображаемого объекта QML в пользовательский интерфейс приложения.

Например, предположим, что есть файл MyItem.qml , который выглядит так:

import QtQuick 2.0

Item {
    width: 100; height: 100
}

Этот документ QML можно загрузить с помощью QQmlComponent или QQuickView с помощью следующего кода C++. Использование QQmlComponent требует вызова QQmlComponent::create() для создания нового экземпляра компонента, в то время как QQuickView автоматически создает экземпляр компонента, который доступен через QQuickView::rootObject():

// Using QQmlComponent
QQmlEngine engine;
QQmlComponent component(&engine,
        QUrl::fromLocalFile("MyItem.qml"));
QObject *object = component.create();
...
delete object;
// Using QQuickView
QQuickView view;
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
QObject *object = view.rootObject();

Это object — экземпляр компонента MyItem.qml, который был создан. Теперь вы можете изменить свойства элемента, используя QObject::setProperty() или QQmlProperty:

object->setProperty("width", 500);
QQmlProperty(object, "width").write(500);

В качестве альтернативы, вы можете привести объект к его фактическому типу и вызвать методы с безопасностью на этапе компиляции. В этом случае базовым объектом MyItem.qml является Item, который определяется классом QQuickItem:

QQuickItem *item = qobject_cast<QQuickItem*>(object);
item->setWidth(500);

Вы также можете подключиться к любым сигналам или вызвать методы, определенные в компоненте, используя QMetaObject::invokeMethod() и QObject::connect(). Дополнительные сведения см. в разделах Вызов методов QML и Подключение к сигналам QML ниже.

Доступ к загруженным объектам QML по имени объекта

Компоненты QML представляют собой в основном древовидные структуры объектов с дочерними элементами, имеющими братьев/сестер и собственные дочерние элементы. Дочерние объекты компонентов QML можно найти, используя свойство QObject::objectName с помощью QObject::findChild(). Например, если корневой элемент в MyItem.qml имел дочерний элемент Rectangle:

import QtQuick 2.0

Item {
    width: 100; height: 100

    Rectangle {
        anchors.fill: parent
        objectName: "rect"
    }
}

Дочерний элемент можно найти так:

QObject *rect = object->findChild<QObject*>("rect");
if (rect)
    rect->setProperty("color", "red");

Обратите внимание, что объект может иметь несколько дочерних элементов с тем же самым objectName. Например, ListView создает несколько экземпляров своего делегата, поэтому, если его делегат объявлен с определенным objectName, ListView будет иметь несколько дочерних элементов с тем же самым objectName. В этом случае можно использовать QObject::findChildren() для поиска всех дочерних элементов с совпадающим objectName.

Предупреждение: Хотя можно использовать C++, чтобы получить доступ к объектам QML и манипулировать ими в глубоких слоях древовидной структуры, мы рекомендуем не применять этот подход за пределами тестирования и прототипирования приложения. Одним из преимуществ интеграции QML и C++ является возможность реализовать пользовательский интерфейс QML отдельно от логики C++ и базы данных, а этот подход нарушается, если сторона C++ добирается до глубоких слоев компонентов QML для их прямого изменения. Это затруднит, например, замену компонента представления QML другим, если в новом компоненте отсутствует необходимый objectName. Лучше, чтобы реализация C++ знала как можно меньше об реализации пользовательского интерфейса QML и структуре объекта QML.

Доступ к членам типа объекта QML из C++

Свойства

Любые свойства, объявленные в объекте QML, автоматически доступны из C++. Учитывая элемент QML, подобный этому:

// MyItem.qml
import QtQuick 2.0

Item {
    property int someNumber: 100
}

Значение свойства someNumber можно задать и прочитать с помощью QQmlProperty или QObject::setProperty() и QObject::property():

QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();

qDebug() << "Property value:" << QQmlProperty::read(object, "someNumber").toInt();
QQmlProperty::write(object, "someNumber", 5000);

qDebug() << "Property value:" << object->property("someNumber").toInt();
object->setProperty("someNumber", 100);

Вы всегда должны использовать QObject::setProperty(), QQmlProperty или QMetaProperty::write() для изменения значения свойства QML, чтобы убедиться, что движок QML проинформирован об изменении свойства. Например, скажем, у вас есть пользовательский тип PushButton со свойством buttonText , которое внутренне отражает значение члена-переменной m_buttonText. Изменение члена-переменной напрямую, как в этом случае, — плохая идея:

//bad code
QQmlComponent component(engine, "MyButton.qml");
PushButton *button = qobject_cast<PushButton*>(component.create());
button->m_buttonText = "Click me";

Поскольку значение изменяется непосредственно, это обходит механизм метаобъектов Qt, и движок QML не получает информации об изменении свойства. Это означает, что привязки свойств к buttonText не будут обновлены, и любые обработчики onButtonTextChanged не будут вызваны.

Вызов методов QML

Все методы QML экспонируются в системе метаобъектов и могут вызываться из C++ с помощью QMetaObject::invokeMethod(). Параметры и возвращаемые значения, передаваемые из QML, всегда преобразуются в значения QVariant в C++.

Вот приложение C++, которое вызывает метод QML с помощью QMetaObject::invokeMethod():

QML
// MyItem.qml
import QtQuick 2.0

Item {
    function myQmlFunction(msg) {
        console.log("Got message:", msg)
        return "some return value"
    }
}
C++
// main.cpp
QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();

QVariant returnedValue;
QVariant msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
        Q_RETURN_ARG(QVariant, returnedValue),
        Q_ARG(QVariant, msg));

qDebug() << "QML function returned:" << returnedValue.toString();
delete object;

Обратите внимание, что аргументы Q_RETURN_ARG() и Q_ARG() для QMetaObject::invokeMethod() должны быть указаны как типы QVariant, так как это универсальный тип данных, используемый для параметров и возвращаемых значений методов QML.

Подключение к сигналам QML

Все сигналы QML автоматически доступны в C++ и могут подключаться с помощью QObject::connect() как любые обычные сигналы Qt C++. В свою очередь, любой сигнал C++ может быть получен объектом QML с помощью обработчиков сигналов.

Вот компонент QML с сигналом под названием qmlSignal , который излучается со строковым параметром. Этот сигнал подключен к слоту объекта C++ с помощью QObject::connect(), так что метод cppSlot() вызывается всякий раз, когда излучается qmlSignal:

// MyItem.qml
import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(string msg)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal("Hello from QML")
    }
}
class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(const QString &msg) {
        qDebug() << "Called the C++ slot with message:" << msg;
    }
};

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject *item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QString)),
                     &myClass, SLOT(cppSlot(QString)));

    view.show();
    return app.exec();
}

Когда тип объекта QML используется в качестве параметра сигнала, параметр должен использовать var в качестве типа, а значение должно быть получено в C++ с использованием типа QVariant:

// MyItem.qml
import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(var anObject)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal(item)
    }
}
class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(const QVariant &v) {
       qDebug() << "Called the C++ slot with value:" << v;

       QQuickItem *item =
           qobject_cast<QQuickItem*>(v.value<QObject*>());
       qDebug() << "Item dimensions:" << item->width()
                << item->height();
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject *item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QVariant)),
                     &myClass, SLOT(cppSlot(QVariant)));

    view.show();
    return app.exec();
}

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.6/qtqml-cppintegration-interactqmlfromcpp.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API