C++ 中如何访问 QML

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

前言

上一章节我们介绍了在 Qt中如何注册一个C++类到qml,也就是在qml中使用C++的类。那么这一章节我们要倒过来,看看在C++中是如何去访问qml的,包括修改qml中控件的属性,以及动态创建qml组件。

Qt Quick 加载QML文档过程解析

先来看一下 Qt Quick 中常用的一些类的继承关系

inherit
inherit
inherit
inherit
inherit
inherit
inherit
inherit
QQuickItem
QObject
QQuickPaintedItem
QWindow
QQuickWindow
QQuickView
QCoreApplication
QGuiApplication
QApplication
inherit
inherit
inherit
inherit
inherit
inherit
QJSEngine
QObject
QQmlEngine
QQmlApplicationEngine
QQmlContext
QQmlComponent
QQmlExpression

介绍几个重要的成员:

  1. QQmlEngine
    每个QML组件都在 QQmlContext 中实例化。QQmlContext 对于向QML组件传递数据至关重要。在QML中上下文按层次结构排列,此层次结构由 QQmlEngine 管理。在创建任何QML组件之前,应用程序必须已创建 QQmlEngine 才能访问QML上下文

  2. QQmlContext
    上下文允许数据暴露给由QML引擎实例化的QML组件。每个 QQmlContext 都包含一组不同于其QObject属性的属性,这些属性允许按名称将数据显式绑定到上下文。通过调用 QQmlContext::setContextProperty() 来定义和更新上下文属性。

  3. QQmlComponent
    组件是可重用的、封装的QML类型,具有定义良好的接口。可以从QML文件创建QQmlComponent实例。

  4. QQmlExpression
    动态执行表达式 QQmlExpression, 允许客户端在C++中利用一个特定的QML上下文执行JavaScript表达式,表达式执行的结果以QVariant的形式返回,并且遵守QML引擎确定的转换规则。

  5. QQuickWindow
    QQuickWindow 提供与 QQuickItems 的场景交互和显示所需的图形场景管理。QQuickWindow总是有一个不可见的根项。若要将项添加到此窗口,请将项重新分配到根项或场景中的现有项。

  6. QQuickView
    这是QQuickWindow的一个子类,当给定主源文件的URL时,它将自动加载和显示QML场景。或者,您可以使用QQmlComponent实例化自己的对象,并将它们放置在手动设置的QQuickWindow中。

Qt Quick 加载一个 QML 对象的条件:

  • 唯一的 一个 QQmlEngine 引擎用于加载QML文件
  • 唯一的 一个 QQmlContext 用于QML对象实例化和表达式执行的上下文环境
  • 一个 QQmlComponent 组件用于实例化第一个QML对象

第一种加载方式:QQmlEngine + QQmlComponent

#include 
#include 
#include 
#include 
#include 

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

    QQmlEngine engine;
    QQmlComponent *component = new QQmlComponent(&engine);

    QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));

    component->loadUrl(QUrl("qrc:/main.qml"));

    if (!component->isReady() ) {
        qWarning("%s", qPrintable(component->errorString()));
        return -1;
    }

    QObject *topLevel = component->create();
    QQuickWindow *window = qobject_cast(topLevel);

    QSurfaceFormat surfaceFormat = window->requestedFormat();
    window->setFormat(surfaceFormat);
    window->show();

    return app.exec();
}

使用 QQmlComponent 来实例化 QML 对象的注意事项:

  1. 要在 QQmlEngine 实例不可用的代码中创建组件实例,可以使用qmlContext() 或qmlEngine()
  2. 必须通过 component.create() 来创建实例
  3. 创建完成之后需要把对象放入窗口下才能显示

第二种加载方式:QQmlApplicationEngine + Window

#include 
#include 

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}


import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
}

QQmlApplicationEngine 是Qt中创建 Qt Quick 工程默认使用的引擎。
QQmlApplicationEngine 联合了 QQmlEngine 和 QmlComponent 去加载单独的QML文件,QQmlApplicationEngine 加载以 Window 为根对象的QML文档,QML文档拥有窗口的完整控制权

第三种加载方式:QQuickView + Item

#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQuickView *view = new QQuickView;
    view->setSource(QUrl("qrc:/main.qml"));
    view->show();
    return app.exec();
}


import QtQuick 2.12

Item {
    visible: true
    width: 640
    height: 480
}

不同于 QQmlApplicationEngine 的是,使用 QQuickView 显示QML文档,对窗口的控制权全部在C++代码

C++中各种加载方式通过 object name 修改qml中的属性

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    // 1. 使用 QQmlApplicationEngine
//    QQmlApplicationEngine engine;
//    const QUrl url(QStringLiteral("qrc:/main.qml"));
//    engine.load(url);

//    QObject *rect = engine.rootObjects().at(0)->findChild("rect");
//    if (rect)
//        rect->setProperty("color", "red");



    // 2. 使用 QQmlEngine + QQmlComponent
//    QQmlEngine engine;
//    QQmlComponent *component = new QQmlComponent(&engine);
//    component->loadUrl(QUrl("qrc:/main.qml"));
//    QObject *topLevel = component->create();
//    QQuickWindow *window = qobject_cast(topLevel);
//    window->show();

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



    // 3. 使用 QQuickView
    QQuickView *view = new QQuickView;
    view->setSource(QUrl("qrc:/main.qml"));
    view->show();

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

    return app.exec();
}


import QtQuick 2.12
import QtQuick.Window 2.12

Item {
    visible: true
    width: 640
    height: 480

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

Qt中的对象是一种树形的结构,对Qt对象树模型不了解的同学可以参考 Qt 对象树。所以我们只要知道了一个节点,就可以通过 findChild 来找到他的子节点。以上三种方式都可以通过 findChild 来找到object然后修改属性。前面两种方式,qml文件的根对象是Window,而最后一种是Item

C++ 调用qml中的 function

import QtQuick 2.12

Item {
    function myQmlFunction(msg) {
        console.log("Got message:", msg)
        return "some return value"
    }
}


#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlEngine engine;
    QQmlComponent component(&engine, "qrc:/main.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;

    return app.exec();
}

运行结果:
qml: Got message: Hello from C++
QML function returned: "some return value"

C++ 连接qml中的信号

myclass.h

#ifndef MYCLASS_H
#define MYCLASS_H

#include 
#include 

class MyClass : public QObject
{
    Q_OBJECT
public:
    explicit MyClass(QObject *parent = nullptr);

signals:

public slots:
    void cppSlot(const QString &msg) {
        qDebug() << "Called the C++ slot with message:" << msg;
    }
};

#endif // MYCLASS_H
main.qml

import QtQuick 2.0

Item {
    width: 100; height: 100

    signal qmlSignal(string msg)

    MouseArea {
        anchors.fill: parent
        onClicked: qmlSignal("Hello from QML")
    }
}
main.cpp

#include 
#include 
#include 
#include 
#include "myclass.h"

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

    QQuickView view(QUrl("qrc:/main.qml"));
    QObject *item = view.rootObject();

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

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

运行结果:
Called the C++ slot with message: "Hello from QML"

你可能感兴趣的:(QML,C++,qml)