目录

1.CPP调用QML

1.1 QMetaObject::invokeMethod调用

1.2 CPP中的信号绑定qml中的槽

2.QML调用CPP

2.1 QML单实例注册

2.2将类对象注册到QML的上下文中

2.3QML信号调用CPP槽

3.QML中注入一个cpp实例

3.1qmlRegisterType

3.2QML_ELEMENT

4.附加属性: QML_ATTACHED


以前写过一篇C++和QML交互的的文章(C++与QML交互总结_qml和c++交互_hsy12342611的博客-CSDN博客),很多网友都在看并提出了一些疑问,本篇结合网上的资料从另外一个角度再重新梳理一下C++与QML的交互。

1.CPP调用QML

1.1 QMetaObject::invokeMethod调用

main.cpp

#include #include // 元对象头文件#include #include int main(int argc, char *argv[]){#if QT_VERSION findChild("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.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Label {objectName: "qml_label"text: "QML Label"font.pixelSize: 25function getText(data) {return text + data}}}}

1.2 CPP中的信号绑定qml中的槽

main.cpp

#include #include // 元对象头文件#include #include #include #include #include #include "person.h"int main(int argc, char *argv[]){#if QT_VERSION setContextProperty("OtPerson", &person);// 先在上下文注入,再加载engine.load(url);//cpp获取qml中的指定对象auto rootObj = engine.rootObjects();// rootObj.first()获取所有对象列表auto button = rootObj.first()->findChild("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 nullptrauto button = rootObj.first()->findChild("qml_button");if (!button) {qDebug() << "button is nullptr";}QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);*/return app.exec();}

main.qml

import QtQuick 2.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0import QtQml 2.15Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Button {objectName: "qml_button"text: "QML button"font.pixelSize: 25property int cal: 1// qml中自定义信号signal coutNum(int num)onClicked: {OtPerson.showInfo()if (0 == cal++ % 10) {coutNum(cal)}}// cpp的信号绑定qml的槽Connections {target: OtPersonfunction onQmlCall() {console.log("cpp call qml")}}Connections {target: OtPersonfunction onQmlCall(data) {console.log("cpp call qml " + data)}}}}}

2.QML调用CPP

2.1 QML单实例注册

main.cpp

#include #include // 元对象头文件#include #include #include "person.h"int main(int argc, char *argv[]){#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);#endifQGuiApplication 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.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0import PersonMudle 1.0Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Button {objectName: "qml_button"text: "QML button"font.pixelSize: 25onClicked: {MyPerson.showInfo()}}}}

2.2将类对象注册到QML的上下文中

main.cpp

#include #include // 元对象头文件#include #include #include #include "person.h"int main(int argc, char *argv[]){#if QT_VERSION setContextProperty("OtPerson", &person);return app.exec();}

main.qml

import QtQuick 2.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Button {objectName: "qml_button"text: "QML button"font.pixelSize: 25onClicked: {OtPerson.showInfo()}}}}

2.3QML信号调用CPP槽

main.cpp

#include #include // 元对象头文件#include #include #include #include #include #include "person.h"int main(int argc, char *argv[]){#if QT_VERSION setContextProperty("OtPerson", &person);//cpp获取qml中的指定对象auto rootObj = engine.rootObjects();// rootObj.first()获取所有对象列表auto button = rootObj.first()->findChild("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 nullptrauto button = rootObj.first()->findChild("qml_button");if (!button) {qDebug() << "button is nullptr";}QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clieckButton);*/return app.exec();}

main.qml

import QtQuick 2.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Button {objectName: "qml_button"text: "QML button"font.pixelSize: 25property int cal: 1// qml中自定义信号signal coutNum(int num)onClicked: {OtPerson.showInfo()if (0 == cal++ % 10) {coutNum(cal)}}}}}

3.QML中注入一个cpp实例

3.1qmlRegisterType

main.cpp

#include #include // 元对象头文件#include #include #include #include #include #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);#endifQGuiApplication app(argc, argv);// 注册一个C++类型到qml中qmlRegisterType("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("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 nullptrauto button = rootObj.first()->findChild("qml_button");if (!button) {qDebug() << "button is nullptr";}QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);*/return app.exec();}

main.qml

import QtQuick 2.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0import QtQml 2.15import TreeMudle 1.0Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Button {objectName: "qml_button"text: tree.name + tree.age + tree.date//Qt.formatDate(tree.date, "yyyy-MM-dd hh:mm:ss")font.pixelSize: 25property int cal: 1// qml中自定义信号signal coutNum(int num)onClicked: {OtPerson.showInfo()if (0 == cal++ % 10) {coutNum(cal)}tree.name = "李四"}// cpp的信号绑定qml的槽Connections {target: OtPersonfunction onQmlCall() {console.log("cpp call qml")}}Connections {target: OtPersonfunction onQmlCall(data) {console.log("cpp call qml " + data)}}}// 使用cpp注入的类型MyTree {id: treename: 'My_Tree'age: 110date: new Date()onNameChanged: {console.log("changed name: " + name)}}}}

3.2QML_ELEMENT

.pro

QT += quickQT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++17 qmltypesQML_IMPORT_NAME = TreeMudleQML_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.0SOURCES += \main.cpp \person.cpp \tree.cppRESOURCES += qml.qrc# Additional import path used to resolve QML modules in Qt Creator's code modelQML_IMPORT_PATH =# Additional import path used to resolve QML modules just for Qt Quick DesignerQML_DESIGNER_IMPORT_PATH =# Default rules for deployment.qnx: target.path = /tmp/$${TARGET}/binelse: unix:!android: target.path = /opt/$${TARGET}/bin!isEmpty(target.path): INSTALLS += targetHEADERS += \person.h \tree.h

main.cpp

#include #include // 元对象头文件#include #include #include #include #include #include "person.h"#include "tree.h"int main(int argc, char *argv[]){#if QT_VERSION setContextProperty("OtPerson", &person);// 先在上下文注入,再加载engine.load(url);//cpp获取qml中的指定对象auto rootObj = engine.rootObjects();// rootObj.first()获取所有对象列表auto button = rootObj.first()->findChild("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 nullptrauto button = rootObj.first()->findChild("qml_button");if (!button) {qDebug() << "button is nullptr";}QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);*/return app.exec();}

main.qml

import QtQuick 2.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0import QtQml 2.15import TreeMudle 1.0Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Button {objectName: "qml_button"text: tree.name + tree.age + tree.date//Qt.formatDate(tree.date, "yyyy-MM-dd hh:mm:ss")font.pixelSize: 25property int cal: 1// qml中自定义信号signal coutNum(int num)onClicked: {OtPerson.showInfo()if (0 == cal++ % 10) {coutNum(cal)}tree.name = "李四"}// cpp的信号绑定qml的槽Connections {target: OtPersonfunction onQmlCall() {console.log("cpp call qml")}}Connections {target: OtPersonfunction onQmlCall(data) {console.log("cpp call qml " + data)}}}// 使用cpp注入的类型Tree {id: treename: 'My_Tree'age: 110date: new Date()onNameChanged: {console.log("changed name: " + name)}}}}

person.h

#ifndef PERSON_H#define PERSON_H#include #include #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_INVOKABLEQ_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 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 #include #include //使用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_ELEMENTpublic: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.附加属性: QML_ATTACHED

使用附加属性:本质上会创建一个附加属性对象

效果如下:

tree.h

#ifndef TREE_H#define TREE_H#include #include #include //使用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_ANONYMOUSpublic: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 #include #include "tree.h"class Person : public QObject{Q_OBJECT//类的附加属性,将Tree中的属性附加到Person类中QML_ATTACHED(Tree)QML_ELEMENTpublic:explicit Person(QObject *parent = nullptr);Person(QString name, int age);// 想要让QML调用函数,函数要加上宏Q_INVOKABLEQ_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 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 #include // 元对象头文件#include #include #include #include #include #include "person.h"#include "tree.h"int main(int argc, char *argv[]){#if QT_VERSION setContextProperty("OtPerson", &person);// 先在上下文注入,再加载engine.load(url);//cpp获取qml中的指定对象auto rootObj = engine.rootObjects();// rootObj.first()获取所有对象列表auto button = rootObj.first()->findChild("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 nullptrauto button = rootObj.first()->findChild("qml_button");if (!button) {qDebug() << "button is nullptr";}QObject::connect(button, &QAbstractButton::clicked, &person, &Person::clickButton);*/return app.exec();}

main.qml

import QtQuick 2.15import QtQuick.Window 2.15import QtQuick.Controls 2.15 as QtCtrlimport QtQuick.Layouts 1.0import QtQml 2.15import PersonMudle 1.0Window {width: 640height: 480visible: truetitle: qsTr("qml和cpp交互总结")Item {id: itemanchors.fill: parentQtCtrl.Button {objectName: "qml_button"text: Person.name + Person.age + Person.datefont.pixelSize: 25property 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: OtPersonfunction onQmlCall() {console.log("cpp call qml")}}Connections {target: OtPersonfunction onQmlCall(data) {console.log("cpp call qml " + data)}}// 使用附加属性:本质上会创建一个附加属性对象Person.name: "赵六"Person.age: 25Person.date: new Date()}}}

.pro

QT += quickQT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++17 qmltypesQML_IMPORT_NAME = PersonMudleQML_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.0SOURCES += \main.cpp \person.cpp \tree.cppRESOURCES += qml.qrc# Additional import path used to resolve QML modules in Qt Creator's code modelQML_IMPORT_PATH =# Additional import path used to resolve QML modules just for Qt Quick DesignerQML_DESIGNER_IMPORT_PATH =# Default rules for deployment.qnx: target.path = /tmp/$${TARGET}/binelse: unix:!android: target.path = /opt/$${TARGET}/bin!isEmpty(target.path): INSTALLS += targetHEADERS += \person.h \tree.h