Interacting with QML Objects from C++

Qt 2017. 6. 11. 18:05

[원문] https://doc.qt.io/qt-5/qtqml-cppintegration-interactqmlfromcpp.html


Interacting with QML Objects from C++

모든 QML 객체 유형은 엔진에 의해 내부적으로 구현되었거나 제 3 자 소스가 정의한 QObject 파생 유형입니다. 즉, QML 엔진은 Qt Meta Object System을 사용하여 모든 QML 객체 유형을 동적으로 인스턴스화하고 생성 된 객체를 검사 할 수 있습니다.


이는 C++ 코드에서 QML 객체를 생성하거나, 시각적으로 렌더링 할 수있는 QML 객체를 표시하거나, 시각적이지 않은 QML 객체 데이터를 C++ 응용 프로그램에 통합 할 때 유용합니다. 일단 QML 객체가 생성되면 속성을 읽고 쓰고 메소드를 호출하고 신호 알림을 받기 위해 C++에서 객체를 검사 할 수 있습니다.

Loading QML Objects from 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 문서는 다음 C++ 코드로 QQmlComponent 또는 QQuickView로로드 될 수 있습니다. QQuickView가 QQuickView::rootObject()를 통해 액세스 할 수있는 구성 요소의 인스턴스를 자동으로 만드는 동안 QQmlComponent를 사용하면 QQmlComponent::create()를 호출하여 구성 요소의 새 인스턴스를 생성해야합니다.

// 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();

이 개체는 생성 된 MyItem.qml 구성 요소의 인스턴스입니다. 이제 QObject::setProperty() 또는 QQmlProperty를 사용하여 항목의 속성을 수정할 수 있습니다.

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

또는 객체를 실제 유형으로 캐스팅하고 컴파일 타임 안전성을 갖는 메소드를 호출 할 수 있습니다. 이 경우 MyItem.qml의 기본 객체는 QQuickItem 클래스에 의해 정의 된 Item입니다.

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

또한 QMetaObject::invokeMethod() 및 QObject::connect()를 사용하여 구성 요소에 정의 된 모든 신호 또는 메서드에 연결할 수 있습니다. 자세한 내용은 아래 QML 메소드 호출 및 QML 신호 연결을 참조하십시오.


Accessing Loaded QML Objects by Object Name

QML 구성 요소는 기본적으로 형제와 자식이있는 자식이있는 객체 트리입니다. QObject 구성 요소의 자식 객체는 QObject::findChild()에서 QObject::objectName 속성을 사용하여 찾을 수 있습니다. 예를 들어, 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++ 통합의 한 가지 강점은 C++ 로직 및 데이터 세트 백엔드와 별도로 QML 사용자 인터페이스를 구현할 수 있다는 점입니다.이 전략은 C++ 측이 직접 조작하는 QML 구성 요소에 깊이 도달하면 중단됩니다. 예를 들어, 새 구성 요소에 필수 objectName이 누락 된 경우 QML보기 구성 요소를 다른보기로 스왑하는 것이 어려울 수 있습니다. C++ 구현에서는 QML 사용자 인터페이스 구현 및 QML 객체 트리 구성에 대해 가능한 한 알지 않는 것이 좋습니다.


Accessing Members of a QML Object Type from C++

Properties

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);

QML 엔진에 속성 변경을 알리려면 항상 QObject::setProperty(), QQmlProperty 또는 QMetaProperty::write()를 사용하여 QML 속성 값을 변경해야합니다. 예를 들어, 내부적으로 m_buttonText 멤버 변수의 값을 반영하는 buttonText 속성이있는 사용자 정의 유형의 PushButton이 있다고 가정 해 보겠습니다. 이처럼 멤버 변수를 직접 수정하는 것은 좋은 생각이 아닙니다.

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

값이 직접 변경 되었기 때문에 Qt의 메타 오브젝트 시스템을 우회하고 QML 엔진은 속성 변경을 인식하지 못합니다. 즉, buttonText에 대한 속성 바인딩은 업데이트되지 않고 onButtonTextChanged 핸들러는 호출되지 않습니다.


Invoking QML Methods

모든 QML 메소드는 메타 오브젝트 시스템에 노출되어 있으며 QMetaObject::invokeMethod()를 사용하여 C++에서 호출 할 수 있습니다. QML에서 전달 된 메서드 매개 변수와 반환 값은 항상 C++의 QVariant 값으로 변환됩니다.


다음은 QMetaObject::invokeMethod()를 사용하여 QML 메서드를 호출하는 C++ 응용 프로그램입니다.

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;

QMetaObject::invokeMethod()에 대한 Q_RETURN_ARG() 및 Q_ARG() 인수는 QML 메서드 매개 변수 및 반환 값에 사용되는 일반 데이터 형식이므로 QVariant 형식으로 지정해야합니다.


Connecting to QML Signals

모든 QML 신호는 자동으로 C++에서 사용할 수 있으며 일반적인 Qt C++ 신호처럼 QObject::connect()를 사용하여 연결할 수 있습니다. 대신 C++ 신호는 신호 처리기를 사용하여 QML 객체에서 수신 할 수 있습니다.


다음은 QML 구성 요소이며 qmlSignal이라는 신호는 문자열 유형 매개 변수로 방출됩니다. 이 신호는 QObject::connect()를 사용하여 C++ 객체의 슬롯에 연결되므로 qmlSignal이 방출 될 때마다 cppSlot() 메서드가 호출됩니다.

// 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를 사용해야하며 값은 QVariant 유형을 사용하여 C++에서 수신되어야합니다.

// 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();
}


: