[Qml & Cpp] Qml和C++通信

395 阅读3分钟

1. 调用方: Cpp, 被调方: Qml

获取qml的组件的属性(qmlProperty)和函数(getQmlText())

Qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    
    Button {
        id : btn
        property string qmlProperty: "我是QML组件的属性"
        text : "测试按钮"
        objectName: "btnQml"    // 需要设置,qml引擎需要找到
         
        function getQmlText(info) {
            return info + " from QML"
        }
    }
}

Cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QMetaObject>
#include <QDebug>

int main()
{
    // ... 省略其他代码
    QQmlApplicationEngine engine;
    
    auto qmlObj = engine.rootObjects().first()->findChild<QObject*>("btnQml");
    if (qmlObj)
    {
        QVariant property = qmlObj->property("qmlProperty");
        QVariant methodRet;
        // Q_RETURN_ARG: qml函数的返回值, Q_ARG: qml函数传入的参数
        QMeatObject::invokeMethod(qmlObj, "getQmlText", Qt::AutoConnection, 
        Qt::AutoConnection, Q_RETURN_ARG(QVariant, methodRet), Q_ARG(QVariant, "test "));
        if (methodRet.isValid())
            qDebug() << methodRet.toString();    // 输出qml函数的返回值
    }    
    // ... 省略其他代码
}

2. 调用方: Qml, 被调方: Cpp

2.1 两种方法,Qml调用Cpp对象的成员函数

第一种: 使用qmlRegisterSingletonInstance(qml需要导入模块)

Cpp

#pragma once

#include <QObject>

class MyObject : public QObject
{
    Q_OBJECT
public:
    explicit MyObject(QObject* parent = nullptr) 
        : MyObject(parent) {}
    ~MyObject() {}
    
    Q_INVOKABLE QString sayHello()   // 必须使用Q_INVOKABLE变为元方法
    {
        return "Hello from Cpp";
    }
};
int main()
{
    // 省略其他代码...
    QQmlApplicationEngine engine;
    QScopedPointer<MyObject> example(new MyObject);
    /*
        参数:
            QtQobjectSingleton: qml需要导入的模块名
            1: 主版本号
            0: 次版本号
            MyObj: qml使用的组件名
            example.get(): 对象实例
    */
    qmlRegisterSingletonInstance("QtQObjectSingleton", 1, 0, "MyObj", example.get());
    
    // 省略其他代码...
}

Qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
// 导入cpp注册的模块
import QtQObjectSingleton 1.0

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

    // 使用Cpp
    Button {
        font.pixelSize: 20
        onClicked: {
            text = MyObj.sayHello()    // 调用Cpp对象的成员函数
        }
    }
}
第二种: 使用setContextProperty(qml不需要导入模块)
int main()
{
    // 省略其他代码...
    QQmlApplicationEngine engine;
    QScopedPointer<MyObject> example(new MyObject);
    engine.rootContext()->setContextProperty("MyObj", example.get());
    
    // 省略其他代码...
}

Qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
//import Qt.example.qobjectSingleton 1.0 // 不需要导入模块名,直接使用

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    
    Button {
        id : btn
        font.pixelSize: 20
        onClicked: {
            text = MyObj.sayHello()
        }
    }
}

2.2 Qml的组件信号和Cpp对象的槽函数绑定

Cpp槽函数的参数如果和qml信号的参数不匹配,需要强转为一致的。但是也有局限,必须能转换成qml支持的类型,qml也就支持以下几种类型

注意: 使用SIGNAL和SLOT宏来进行绑定,否则使用新版本的信号槽绑定会有类型不匹配的错误。

Cpp

#pragma once

#include <QObject>
#include <QDebug>

class MyObject : public QObject
{
    Q_OBJECT
public:
    explicit MyObject(QObject *parent = nullptr) 
        : QObject(parent) {}
    ~MyObject() {}
    
public slots:
    void printArg(QString arg)
    {
        qDebug() << "output arg: " << arg;
    }
};
// 省略其他代码...

int main()
{
    // 省略其他代码...
    
    QQmlApplicationEngine engine;
    MyObject myObj;
    auto obj = engine.rootObjects().first()->findChild<QObject*>("btnQml");
    if (obj)
    {
        // 都转换为QString类型,qml的string类型可以直接转换成QString类型
        // 
        QObject::connect(obj, SIGNAL(clickArg(QString)), &myObj, SLOT(printArg(QString)));
    }
    
    // 省略其他代码...
}

2.2 Qml处理Cpp的信号

Qml提供了Connections类处理Cpp的信号,前提必须Cpp对象注册到qml引擎中

Cpp

#include <QObject>

class TestObj : public QObject
{
    Q_OBJECT
public:
    TestObj(QObject* parent = nullptr) 
        : QObject(parent) {}
    ~TestObj() {}
    
// 信号
signals:
    void signalFunc();
};
// 省略其他代码...

int main()
{
    // 省略其他代码...
    QQmlApplicationEngine engine;
    TestObj obj;
    // 注册到Qml引擎中
    engine.rootContext()->setContextProperty("TestObj", &obj);
    
    // 发射TestObj的signalFunc()信号
    emit obj.signalFunc();
    
    // 省略其他代码...
}

Qml

// 省略其他代码...

Connections {
    target: TestObj    // cpp对象注册到qml引擎的名字
    // 槽函数
    function onSingnalFunc() {    // on + cpp的信号名,第一个字母必须大写
        console.log("接收到了cpp的信号")
    }
}