c++访问QML中的控件

  1. main.qml如下所示:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    // 定义一个带参信号
    signal sigTest(value: string, count: int)

    // sigTest信号的信号处理器
    onSigTest: {
        console.log(value, count)
    }

    function func() {
        return "func"
    }

}

  1. main.cpp代码如下所示:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlProperty>

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    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);

    // 根据QQmlApplicationEngine获取对象树根节点
    QObject* obj = engine.rootObjects().first();
    // 修改QML中控件的属性--更改标题
    QQmlProperty(obj, "title").write("test");
    // 调用QML中控件的信号
    QMetaObject::invokeMethod(obj, "sigTest", Q_ARG(QString, "call onSigTest"), Q_ARG(int, 1024));
    // 调用QML中控件的方法
    QVariant ret;
    QMetaObject::invokeMethod(obj, "func", Q_RETURN_ARG(QVariant, ret));
    // 获取方法返回值
    qDebug() << ret;    

    return app.exec();
}

QML访问c++中类的属性和方法

方式1:将c++类型注册为QML系统可以使用的类型

这种方式下,使用注册成功的C++类型与使用其他控件类型一样,进行声明式创建实例

  1. 示例类如下:
#ifndef TEST_H
#define TEST_H

#include <QObject>

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

    Q_INVOKABLE int age() const;
    Q_INVOKABLE void setAge(int newAge);

signals:

    void ageChanged();

private:
    int m_age = 24;

    Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged FINAL)
};

#endif // TEST_H

#include "test.h"

Test::Test(QObject *parent)
    : QObject{parent}
{}

int Test::age() const
{
    return m_age;
}

void Test::setAge(int newAge)
{
    if (m_age == newAge)
        return;
    m_age = newAge;
    emit ageChanged();
}

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlProperty>

#include "test.h"

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    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);

    // 在QML类型中注册c++类型
    qmlRegisterType<Test>("Test",1, 0, "Test");

    engine.load(url);

    return app.exec();
}

  1. qml示例如下:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import Test 1.0

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

    Rectangle {
        width: 100
        height: 20
        Text {
            id: name
            text: test.age      // 使用cpp类中的属性
            anchors.centerIn: parent
            Component.onCompleted: {
                test.age = 1024
                test.setAge(1026)
            }
        }
        anchors.centerIn: parent
        color: "yellow"

    }

    Test {
        id: test
    }
}

方式2:将c++实例注册到 QML 系统中

示例如下:

  1. myviewmodel.h
#ifndef MYVIEWMODEL_H
#define MYVIEWMODEL_H

#include <QObject>
#include <QString>

class MyViewModel : public QObject
{
    Q_OBJECT
    //--------提供给QML访问的属性--------
    // FINAL关键字表示禁止子类重写该属性
    Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged FINAL)

public:
    MyViewModel(QObject* parent = nullptr);
    ~MyViewModel();

    QString source();
    void setSource(QString source);
    //---------提供给QML调用的函数接口--------
    Q_INVOKABLE void on_btn_clicked();
signals:
    void sourceChanged(const QString& source);

private:
    QString m_source;

};

#endif // MYVIEWMODEL_H
  1. myviewmodel.cpp
#include "myviewmodel.h"

#include <QDebug>

MyViewModel::MyViewModel(QObject* parent) : QObject(parent) {}


MyViewModel::~MyViewModel() {

}

QString MyViewModel::source() {
    return m_source;
}

void MyViewModel::setSource(QString source) {
    qDebug() << "修改前,source值为:" << this->m_source;
    if (this->m_source != source) {
        this->m_source = source;

        // 重要,触发页面更新
        emit sourceChanged(this->m_source);
    }

}

void MyViewModel::on_btn_clicked() {
    qDebug() << "组件内部按钮点击事件的执行动作";
}
  1. main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>

#include "myviewmodel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    // QQuickStyle::setStyle("Material");
    QQmlApplicationEngine engine;
    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreationFailed,
        &app,
        []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);
    // 将 C++ 实例注册到 QML 系统中
    MyViewModel* viewModel = new MyViewModel(&app);
    qmlRegisterSingletonInstance("nrvcer.test", 1, 0, "MyViewModel", viewModel);
    engine.loadFromModule("test_wrap_components", "Main");

    return app.exec();
}
  1. CustomComponents.qml
import QtQuick

Item {
    id: root

    property string source  // 属性接收图片源
    signal pass()


    Image {
        id: image
        width: 300
        height: 300
        source: root.source
        fillMode: Image.PreserveAspectFit
        anchors.top:  parent.top
        anchors.horizontalCenter: parent.horizontalCenter
    }
    Item {
        id: item
        width:  200
        height: 100
        anchors.top: image.bottom
        anchors.topMargin: 20
        anchors.horizontalCenter: parent.horizontalCenter
        Rectangle {
            id: rec1
            anchors.fill: parent
            radius: 20
            Text {
                text: "向组件使用者发出信号,让上层决定按钮行为"
                anchors.centerIn: parent
                wrapMode: Text.WrapAnywhere
            }
            MouseArea {
                anchors.fill: parent
                enabled: true
                // 触发信号,等同于emit pass()
                onClicked:root.pass()
            }
        }


    }
    Item {
        width:  200
        height: 100
        anchors.top: item.bottom
        anchors.topMargin: 20
        anchors.horizontalCenter: parent.horizontalCenter
        Rectangle {
            anchors.topMargin: 20
            anchors.fill: parent
            radius: 20
            Text {
                text: "测试修改source"
                anchors.centerIn: parent
            }
            MouseArea {
                anchors.fill: parent
                enabled: true

                onClicked: {
                    // 这里会调用MyViewModel的setSource方法
                    viewModel.source = "images/back.png"
                }
            }
        }


    }

}
  1. Main.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import test_wrap_components
import nrvcer.test 1.0

ApplicationWindow {
    width:  1920
    height: 1080
    visible: true

    property var viewModel: MyViewModel

    ColumnLayout {
        anchors.horizontalCenter: parent.horizontalCenter
        CustomComponents {
            id: customComponents
            source: viewModel.source
            onPass: viewModel.on_btn_clicked()
        }
    }

}