QML调用C++的三种方法

1.注册法
由于QML引擎与Qt元对象系统的紧密集成,可以从QML代码访问由QObject派生的类适当公开的任何功能。这使得C ++类的属性和方法可以直接从QML访问,通常很少或无需修改。

QML引擎能够通过元对象系统内省QObject实例。这意味着,任何QML代码都可以访问QObject派生类实例的以下成员:

属性(使用Q_PROPERTY注册的属性)
方法(需注册为public slots或是标记为Q_INVOKABLE)
信号
(此外,如果已使用Q_ENUMS声明枚举,则可以使用枚举。)

通常,无论是否已向QML类型系统注册了QObject派生类,都可以从QML访问它们。但是,如果QML引擎要访问其他类型信息(例如,如果要将类本身用作方法参数或属性,或者要将其中一个枚举类型用于以这种方式使用),那么该类可能需要注册。

#ifndef CPPOBJECT_H
#define CPPOBJECT_H

#include <QObject>

//派生自QObject
//使用qmlRegisterType注册到QML中
class CppObject : public QObject
{
Q_OBJECT
//注册属性,使之可以在QML中访问--具体语法百度Q_PROPERTY
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
Q_PROPERTY(int year READ getYear WRITE setYear NOTIFY yearChanged)

public:
explicit CppObject(QObject *parent = nullptr);
//通过Q_INVOKABLE宏标记的public函数可以在QML中访问
Q_INVOKABLE void sendSignal();//功能为发送信号

//给类属性添加访问方法--myName
void setName(const QString &name);
QString getName() const;
//给类属性添加访问方法--myYear
void setYear(int year);
int getYear() const;

signals:
//信号可以在QML中访问
void cppSignalA();//一个无参信号
void cppSignalB(const QString &str,int value);//一个带参数信号
void nameChanged(const QString name);
void yearChanged(int year);

public slots:
//public槽函数可以在QML中访问
void cppSlotA();//一个无参槽函数
void cppSlotB(const QString &str,int value);//一个带参数槽函数

private:
//类的属性
QString myName;
int myYear;
};

#endif // CPPOBJECT_H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
在头文件中,我定义了信号和public槽函数,以及Q_INVOKABLE宏标记的public函数,还通过Q_PROPERTY注册了两个属性,这些方法和属性之后都可以在QML中进行访问。

#include "CppObject.h"

#include <QDebug>

CppObject::CppObject(QObject *parent)
: QObject(parent),
myName("none"),
myYear(0)
{

}

void CppObject::sendSignal()
{
//测试用,调用该函数后发送信号
qDebug()<<"CppObject::sendSignal";
emit cppSignalA();
emit cppSignalB(myName,myYear);
}

void CppObject::setName(const QString &name)
{
qDebug()<<"CppObject::setName"<<name;
if(myName!=name){
qDebug()<<"emit nameChanged";
myName=name;
emit nameChanged(name);
}
}

QString CppObject::getName() const
{
qDebug()<<"CppObject::getName";
return myName;
}

void CppObject::setYear(int year)
{
qDebug()<<"CppObject::setYear"<<year;
if(year!=myYear){
qDebug()<<"emit yearChanged";
myYear=year;
emit yearChanged(myYear);
}
}

int CppObject::getYear() const
{
qDebug()<<"CppObject::getYear";
return myYear;
}

void CppObject::cppSlotA()
{
qDebug()<<"CppObject::cppSlotA";
}

void CppObject::cppSlotB(const QString &str, int value)
{
qDebug()<<"CppObject::cppSlotB"<<str<<value;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
在QML项目中添加类CppObject,并在QQmlApplicationEngine之前注册

函数原型
template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);

单例
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
1
2
3
4
5
6
7
8
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "CppObject.h"

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

QGuiApplication app(argc, argv);

//qmlRegisterType注册C++类型至QML
//arg1:import时模块名
//arg2:主版本号
//arg3:次版本号
//arg4:QML类型名
qmlRegisterType<CppObject>("MyCppObject",1,0,"CppObject");

QQmlApplicationEngine engine;

//也可以注册为qml全局对象
//engine.rootContext()->setContextProperty("cppObj",new CppObject(qApp));

engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;

return app.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
通过使用qmlRegisterType,将刚才定义的QObject派生类注册到QML中(Qt5.15增加了新的注册方式)。

import QtQuick 2.9
import QtQuick.Window 2.9
//引入我们注册的模块
import MyCppObject 1.0

Window {
id: root
visible: true
width: 500
height: 300
title: qsTr("QML调用Cpp对象")
color:"green"

signal qmlSignalA
signal qmlSignalB(string str,int value)

//鼠标点击区域
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
//测试时点击左键或右键
onClicked: {
if(mouse.button===Qt.LeftButton){
console.log('----qml 点击左键:Cpp发射信号')
cpp_obj.name="CSDN" //修改属性会触发set函数,获取值会触发get函数
cpp_obj.year=2020
cpp_obj.sendSignal() //调用Q_INVOKABLE宏标记的函数
}else{
console.log('----qml 点击右键:QML发射信号')
root.qmlSignalA()
root.qmlSignalB('CSDN',2020)
}
}
}

//作为一个QML对象
CppObject{
id:cpp_obj
//也可以像原生QML对象一样操作,增加属性之类的
property int counts: 0
//值改变信号对应的槽函数,on + * 方式信号槽,自动连接
onYearChanged: {
counts++
console.log('qml onYearChanged',counts)
}
onCountsChanged: {
console.log('qml onCountsChanged',counts)
}
}

//组件加载完成执行
Component.onCompleted: {
//关联信号与信号处理函数的方式同QML中的类型
//Cpp对象的信号关联到Qml
//cpp_obj.onCppSignalA.connect(function(){console.log('qml signalA process')})
cpp_obj.onCppSignalA.connect(()=>console.log('qml signalA process')) //js的lambda
cpp_obj.onCppSignalB.connect(processB)
//Qml对象的信号关联到Cpp
root.onQmlSignalA.connect(cpp_obj.cppSlotA)
root.onQmlSignalB.connect(cpp_obj.cppSlotB)
}

//定义的函数可以作为槽函数
function processB(str,value){
console.log('qml function processB',str,value)
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
注册之后就能直接在QML中使用刚才定义的C++类型了,并且可以像QML定义的类型一样进行操作,如信号槽关联、属性绑定等

2.插件法
在Qt新项目向导下创建项目Library->Qt Quick 2 Extension Plugin
输入你的项目名称(此例为QMLPluginTemp)后, 创建项目会自动生成两个.cpp两个.h和一个qmldir文件
修改qmldir文件如下,module项名称就是后面导入的模块名称,和变量命名一样,不要有.或者其他特殊符号;plugin项就是新建插件的项目名称

qmldir 文件

module MyPlugin

plugin QMLPluginTemp
1
2
3
4
5
#ifndef MYITEM_H
#define MYITEM_H

#include <QQuickItem>

class MyItem : public QQuickItem
{
Q_OBJECT
Q_DISABLE_COPY(MyItem)

public:
MyItem(QQuickItem *parent = 0);
~MyItem();

Q_INVOKABLE void read();

};

#endif // MYITEM_H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "myitem.h"
#include <QDebug>
MyItem::MyItem(QQuickItem *parent):
QQuickItem(parent)
{
// By default, QQuickItem does not draw anything. If you subclass
// QQuickItem to create a visual item, you will need to uncomment the
// following line and re-implement updatePaintNode()

// setFlag(ItemHasContents, true);
}

MyItem::~MyItem()
{
}


void MyItem::read()
{

qWarning() << "qml cpp plugin ";
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#pragma once

#include <QQmlExtensionPlugin>

class QMLPluginTempPlugin: public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")

public:
void registerTypes(const char *uri);
};

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "qmlplugintemp_plugin.h"
#include "myitem.h"

#include <qqml.h>

void QMLPluginTempPlugin::registerTypes(const char *uri)
{
// @uri com.mycompany.qmlcomponents
qmlRegisterType<MyItem>(uri, 1, 0, "MyItem");
}

1
2
3
4
5
6
7
8
9
10
11
构建项目后,在生成目录下找到qmldir文件和QMLPluginTemp.dll或QMLPluginTempd.dll(根据生成类型,debug还是release),放到一个新建文件夹MyPlugin(qmldir文件中的模块名)中,再将MyPlugin文件夹放入新建文件夹DLL中。
main函数中指定DLL文件夹路径

#include <QGuiApplication>
#include <QQmlApplicationEngine>

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

QQmlApplicationEngine engine;
engine.addImportPath("D:/QML/DLL/");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

return app.exec();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
随后就可以在QML文件中使用插件
如果不指定,在Qt安装路径如D:\ProgramFiles\Qt\Qt580\5.8\mingw53_32\qml目录新建文件夹MyPlugin,把QMLPluginTemp.dll,QMLPluginTempd.dll和qmldir复制到MyPlugin文件夹内。

import QtQuick 2.6
import QtQuick.Window 2.2
import MyPlugin 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")

MainForm {
anchors.fill: parent
mouseArea.onClicked: {
cppPlugin.read()
console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
}

}

MyItem{
id:cppPlugin
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
3.暴露法
使用 setContextProperty,是将对象或数据暴露给 QML,一般默认就是全局单例。 函数原型

void QQmlContext::setContextProperty(const QString &name, QObject *value)
void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
1
2
int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QQmlApplicationEngine engine;
engine.rootContext( )->setContextProperty(
"Greeting",
QObject::tr( "Hello QML from C++" ) );//变量,qml中直接就可用此属性的值

engine.rootContext( )->setContextProperty(
"CPlusPlusClass",
new CPlusPlusClass() );//对象,此处new的时候创建对象了
//setContextProperty,在qml中是全局的
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

return app.exec();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Window {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
Text {
id: text
text: Greeting //直接就可以使用此属性
anchors.centerIn: parent
}
Button {

anchors.top: text.bottom
text: "CPlusPlusClass.method()"
MouseArea {

anchors.fill: parent
onClicked: {
CPlusPlusClass.method()//调用类中的方法
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
引用链接
QML与C++交互
————————————————
版权声明:本文为CSDN博主「52_赫兹的鲸」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40194498/article/details/109592155

 

posted @ 2023-02-04 14:51  imxiangzi  阅读(915)  评论(0编辑  收藏  举报