Summary of interaction between C++ and QML 2

Table of contents

1.CPP calls QML

1.1 QMetaObject::invokeMethod call

1.2 Signals in CPP are bound to slots in qml

2.QML calls CPP

2.1 QML single instance registration

2.2 Register the class object into the QML context

2.3 QML signal calls CPP slot

3. Inject a cpp instance into QML

3.1qmlRegisterType

3.2QML_ELEMENT

4. Additional attributes: QML_ATTACHED


I have written an article about the interaction between C++ and QML before ( Summary of interaction between C++ and QML_interaction between qml and c++_hsy12342611's blog - CSDN blog ). Many netizens have read it and raised some questions. This article combines the information on the Internet from Let’s review the interaction between C++ and QML from another perspective.

1.CPP calls QML

1.1 QMetaObject::invokeMethod call

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>


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

    //cpp调用qml指定对象的指定方法
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto label = rootObj.first()->findChild<QObject*>("qml_label");

    // 通过元对象调用
    QVariant ret;
    QMetaObject::invokeMethod(label, "getText",
                              Q_RETURN_ARG(QVariant, ret),
                              Q_ARG(QVariant, " hello"));

    qDebug() << ret.toString();

    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Label {
            objectName: "qml_label"
            text: "QML Label"
            font.pixelSize: 25

            function getText(data) {
                return text + data
            }
        }
    }

}

1.2 Signals in CPP are bound to slots in qml

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.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);

    Person person("张三", 18);

    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的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }
        }
    }

}

2.QML calls CPP

2.1 QML single instance registration

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include "person.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);

    Person person("张三", 18);
    // qml单实例注册
    qmlRegisterSingletonInstance("PersonMudle", 1, 0, "MyPerson", &person);

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

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import PersonMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25

            onClicked: {
                MyPerson.showInfo()
            }
        }
    }

}

2.2 Register the class object into the QML context

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include "person.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);

    Person person("张三", 18);

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

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25

            onClicked: {
                OtPerson.showInfo()
            }
        }
    }

}

2.3 QML signal calls CPP slot

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.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);

    Person person("张三", 18);

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

    //上下文: 将类对象注册到QML的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clieckButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: "QML button"
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
            }
        }
    }

}

3. Inject a cpp instance into QML

3.1qmlRegisterType

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"
#include "tree.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);

    // 注册一个C++类型到qml中
    qmlRegisterType<Tree>("TreeMudle", 1, 0, "MyTree");

    Person person("张三", 18);

    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的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15
import TreeMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: tree.name + tree.age + tree.date//Qt.formatDate(tree.date, "yyyy-MM-dd hh:mm:ss")
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
                tree.name = "李四"
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }
        }

        // 使用cpp注入的类型
        MyTree {
            id: tree
            name: 'My_Tree'
            age: 110
            date: new Date()

            onNameChanged: {
                console.log("changed name: " + name)

            }
        }
    }

}

3.2QML_ELEMENT

.pro

QT += quick

QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17 qmltypes

QML_IMPORT_NAME = TreeMudle
QML_IMPORT_MAJOR_VERSION = 1

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp \
        person.cpp \
        tree.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

HEADERS += \
    person.h \
    tree.h

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"
#include "tree.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);

    Person person("张三", 18);

    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的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15
import TreeMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: tree.name + tree.age + tree.date//Qt.formatDate(tree.date, "yyyy-MM-dd hh:mm:ss")
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
                tree.name = "李四"
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }
        }

        // 使用cpp注入的类型
        Tree {
            id: tree
            name: 'My_Tree'
            age: 110
            date: new Date()

            onNameChanged: {
                console.log("changed name: " + name)

            }
        }
    }

}

person.h

#ifndef PERSON_H
#define PERSON_H

#include <QObject>
#include <QString>
#include "tree.h"

class Person : public QObject
{
    Q_OBJECT
    //类的附加属性
    QML_ATTACHED(Tree)

public:
    explicit Person(QObject *parent = nullptr);
    Person(QString name, int age);
    // 想要让QML调用函数,函数要加上宏Q_INVOKABLE
    Q_INVOKABLE void showInfo() const noexcept;

public slots:
    void clickButton() const noexcept;
    void clickCal(int data) const noexcept;

signals:
    void qmlCall() const;
    // cpp给qml传参数不是所有类型都可以,一般就字符串,json和基本类型可以
    void qmlCall(QString) const;

private:
    QString _name;
    int _age;

};

#endif // PERSON_H

person.cpp

#include "person.h"
#include <QDebug>

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

}

Person::Person(QString name, int age) {
    _name = name;
    _age = age;
}

void Person::showInfo() const noexcept {
    qDebug() << "name: " << _name << ", age: "<< _age;

    // cpp发送信号调用qml中的槽
    emit qmlCall();
    emit qmlCall("王五");
}

void Person::clickButton() const noexcept {
    qDebug() << __FUNCTION__ << " in qml: click button";
}

void Person::clickCal(int data) const noexcept {
    qDebug() << __FUNCTION__ << "  " << data;
}

tree.h

#ifndef TREE_H
#define TREE_H

#include <QObject>
#include <QDate>
#include <QtQml>

//使用QML_ELEMENT后,就会产生元数据类型:描述数据的数据

class Tree : public QObject
{
    Q_OBJECT
    // qml中使用cpp类的属性
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
    Q_PROPERTY(QDate date READ getDate WRITE setDate NOTIFY dateChanged)

    QML_ELEMENT

public:
    explicit Tree(QObject *parent = nullptr);
    Tree(QString nme, qint32 age, QDate date);

    void setName(QString name) noexcept;
    void setAge(qint32 age) noexcept;
    void setDate(QDate date) noexcept;

    QString getName() const noexcept;
    qint32 getAge() const noexcept;
    QDate getDate() const noexcept;

signals:
    void nameChanged(QString);
    void ageChanged();
    void dateChanged();

private:
    QString _name;
    qint32 _age;
    QDate _date;
};

#endif // TREE_H

tree.cpp

#include "tree.h"

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

}

Tree::Tree(QString name, qint32 age, QDate date) {
    _name = name;
    _age = age;
    _date = date;
}

void Tree::setName(QString name) noexcept {
    _name = name;
    emit nameChanged(name);
}

void Tree::setAge(qint32 age) noexcept {
    _age = age;
    emit ageChanged();
}

void Tree::setDate(QDate date) noexcept {
    _date = date;
    emit dateChanged();
}

QString Tree::getName() const noexcept {
    return _name;
}

qint32 Tree::getAge() const noexcept {
    return _age;
}

QDate Tree::getDate() const noexcept {
    return _date;
}

4. Additional attributes: QML_ATTACHED

Using attached properties: essentially creates an attached properties object

The effect is as follows:

tree.h

#ifndef TREE_H
#define TREE_H

#include <QObject>
#include <QDate>
#include <QtQml>

//使用QML_ELEMENT后,就会产生元数据类型:描述数据的数据

class Tree : public QObject
{
    Q_OBJECT
    // qml中使用cpp类的属性
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
    Q_PROPERTY(QDate date READ getDate WRITE setDate NOTIFY dateChanged)

    QML_ANONYMOUS

public:
    explicit Tree(QObject *parent = nullptr);
    Tree(QString nme, qint32 age, QDate date);

    void setName(QString name) noexcept;
    void setAge(qint32 age) noexcept;
    void setDate(QDate date) noexcept;

    QString getName() const noexcept;
    qint32 getAge() const noexcept;
    QDate getDate() const noexcept;

signals:
    void nameChanged(QString);
    void ageChanged();
    void dateChanged();

private:
    QString _name;
    qint32 _age;
    QDate _date;
};

#endif // TREE_H

tree.cpp

#include "tree.h"

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

}

Tree::Tree(QString name, qint32 age, QDate date) {
    _name = name;
    _age = age;
    _date = date;
}

void Tree::setName(QString name) noexcept {
    _name = name;
    emit nameChanged(name);
}

void Tree::setAge(qint32 age) noexcept {
    _age = age;
    emit ageChanged();
}

void Tree::setDate(QDate date) noexcept {
    _date = date;
    emit dateChanged();
}

QString Tree::getName() const noexcept {
    return _name;
}

qint32 Tree::getAge() const noexcept {
    return _age;
}

QDate Tree::getDate() const noexcept {
    return _date;
}

person.h

#ifndef PERSON_H
#define PERSON_H

#include <QObject>
#include <QString>
#include "tree.h"

class Person : public QObject
{
    Q_OBJECT
    //类的附加属性,将Tree中的属性附加到Person类中
    QML_ATTACHED(Tree)
    QML_ELEMENT

public:
    explicit Person(QObject *parent = nullptr);
    Person(QString name, int age);
    // 想要让QML调用函数,函数要加上宏Q_INVOKABLE
    Q_INVOKABLE void showInfo() const noexcept;

public slots:
    void clickButton() const noexcept;
    void clickCal(int data) const noexcept;

    static Tree* qmlAttachedProperties(QObject*);

signals:
    void qmlCall() const;
    // cpp给qml传参数不是所有类型都可以,一般就字符串,json和基本类型可以
    void qmlCall(QString) const;

private:
    QString _name;
    int _age;

};

#endif // PERSON_H

person.cpp

#include "person.h"
#include <QDebug>

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

}

Person::Person(QString name, int age) {
    _name = name;
    _age = age;
}

void Person::showInfo() const noexcept {
    qDebug() << "name: " << _name << ", age: "<< _age;

    // cpp发送信号调用qml中的槽
    emit qmlCall();
    emit qmlCall("王五");
}

void Person::clickButton() const noexcept {
    qDebug() << __FUNCTION__ << " in qml: click button";
}

void Person::clickCal(int data) const noexcept {
    qDebug() << __FUNCTION__ << "  " << data;
}

Tree* Person::qmlAttachedProperties(QObject* obj) {
    qDebug() << __FUNCTION__ << obj;
    return new Tree(obj);
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
// 元对象头文件
#include <QMetaObject>
#include <QDebug>
#include <QQmlContext>
#include <QAbstractButton>
#include <QPushButton>
#include "person.h"
#include "tree.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);

    Person person("张三", 18);

    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的上下文背景中
    auto ctext = engine.rootContext();
    ctext->setContextProperty("OtPerson", &person);

    // 先在上下文注入,再加载
    engine.load(url);

    //cpp获取qml中的指定对象
    auto rootObj = engine.rootObjects();
    // rootObj.first()获取所有对象列表
    auto button = rootObj.first()->findChild<QObject*>("qml_button");
    // 使用QT4的方式绑定信号和槽: qml的信号,cpp的槽
    QObject::connect(button, SIGNAL(clicked()), &person, SLOT(clickButton()));
    QObject::connect(button, SIGNAL(coutNum(int)), &person, SLOT(clickCal(int)));
    /*
    // 使用QT5的方式绑定信号和槽不可行,此处button is nullptr
    auto button = rootObj.first()->findChild<QPushButton*>("qml_button");
    if (!button) {
        qDebug() << "button is nullptr";
    }
    QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);
    */


    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QtCtrl
import QtQuick.Layouts 1.0
import QtQml 2.15
import PersonMudle 1.0


Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("qml和cpp交互总结")

    Item {
        id: item
        anchors.fill: parent

        QtCtrl.Button {
            objectName: "qml_button"
            text: Person.name + Person.age + Person.date
            font.pixelSize: 25
            property int cal: 1
            // qml中自定义信号
            signal coutNum(int num)

            onClicked: {
                OtPerson.showInfo()
                if (0 == cal++ % 10) {
                    coutNum(cal)
                }
                console.log(Person.name + " " + Person.age + " " + Person.date)
                console.log(this)
            }

            // cpp的信号绑定qml的槽
            Connections {
                target: OtPerson
                function onQmlCall() {
                    console.log("cpp call qml")
                }
            }

            Connections {
                target: OtPerson
                function onQmlCall(data) {
                    console.log("cpp call qml " + data)
                }
            }

            // 使用附加属性:本质上会创建一个附加属性对象
            Person.name: "赵六"
            Person.age: 25
            Person.date: new Date()
        }
    }

}

.pro

QT += quick

QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17 qmltypes

QML_IMPORT_NAME = PersonMudle
QML_IMPORT_MAJOR_VERSION = 1

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp \
        person.cpp \
        tree.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

HEADERS += \
    person.h \
    tree.h

Guess you like

Origin blog.csdn.net/hsy12342611/article/details/133309866