0


【Qt- C++ & Qml 交互】

Qt编程指南 VX:hao541022348

■ 将C++对象注册到 QML中,在QML使用C++对象(Q_INVOKABLE宏)

在 C++ 里定义了一个对象,然后将这个对象注册到 QML 里面。在 QML 里面访问的就是 C++ 定义的对象。

■ C++对象注册到元对象系统

QQmlApplicationEngine::rootContext()->setContextProperty()

■ Q_INVOKABLE 宏

Q_INVOKABLE
Q_INVOKABLE 是个宏定义,这个宏将 函数 声明为元对象系统可调用的函数

  • Q_INVOKABLE 是个宏定义
  • 这个宏定义 针对的是 函数, 不是变量
  • 经过Q_INVOKABLE 声明过得函数 可以被元对象系统调用
  • QtQuick 也在元对象系统内,所以在 QML 中也可以访问这个被声明了的函数

■ 演示步骤

  1. 新建qml工程,里面只有一个main.cpp 与一个默认的 main.qml
  2. 创建一个C++ 的类 MyQmlClass 来与 QML 进行交互(重点关注 函数 Q_INVOKABLE宏
  3. 将 C++ 的对象,注册到 QML中去 QQmlApplicationEngine::rootContext()->setContextProperty()
  4. 做完了这两步,我们就做了 C++ 的工作,在 QML 中 就可以 直接拿到对象 myQmlImp 来调用使用 Q_INVOKABLE 定义的方法了。
  5. 改造 QML (定义一个“获取”的 Button 与 显示获取值得 Label, 点击 Button 就会获取 C++ 中的 m_value 值, 在 Lable 上进行展示。)

MyQmlClass.h

#ifndefMYQMLCLASS_H#defineMYQMLCLASS_H#include<QObject>classMyQmlClass:publicQObject{
    Q_OBJECT
public:explicitMyQmlClass(QObject *parent =nullptr);
 
    Q_INVOKABLE voidsetValue(int value);//这个宏将 函数 声明为元对象系统可调用的函数
    Q_INVOKABLE intgetValue();//这个宏将 函数 声明为元对象系统可调用的函数
 
signals:private:int m_Value;};#endif// MYQMLCLASS_H

MyQmlClass.cpp

#include"MyQmlClass.h"MyQmlClass::MyQmlClass(QObject *parent):QObject(parent){}voidMyQmlClass::setValue(int value){
    m_Value = value;}intMyQmlClass::getValue(){return m_Value;}

打开 main.cpp ,通过 QML 引擎 QQmlApplicationEngine 进行注册。

#include<QGuiApplication>#include<QQmlApplicationEngine>#include<QQmlContext>#include"MyQmlClass.h"intmain(int argc,char*argv[]){QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 
    QGuiApplication app(argc, argv); 
    QQmlApplicationEngine engine;/////声明 对象  首先定义了一个C++ 的对象 myQmlImp ,
    MyQmlClass myQmlImp;//将对象进行注册到QML中//key :自定义字符串,为了好记,我们这里叫做对象的名字 "myQmlImp"//value : 对象引用,对象指针,这里就是&myQmlImp
    engine.rootContext()->setContextProperty("myQmlImp",&myQmlImp);/// 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我们的思路很简单,就是 QML 中来调用上面 C++ 暴露出来的读写函数。所以我们在QML 中定义一个 “获取” Button ,点击它我们就来调用C++中的 getValue() 函数,然后我们需要一个Label 将获取的 C++ 的值进行展示

importQtQuick2.12importQtQuick.Window2.12importQtQuick.Controls2.3
 
Window {
    visible:true
    width:640
    height:480
    title:qsTr("Hello World")
 
    Label{//Label用于显示获取C++的值
        id: label                  //显示控件,唯一标识ID:label
        text:""//初始化内容清空
 
        anchors.bottom: getBtn.top //显示控件的下方放到btn的上方
        anchors.left: getBtn.left  //显示控件的左方与btn的左侧对齐}
 
    Button{//Button 用于获取值
        id: getBtn                 //按钮控件,唯一标识ID:getBtn
        text:"获取"//按钮显示文字
        width:120//按钮宽度
        height:40//按钮高度
 
        anchors.centerIn: parent   //按钮放到窗口中心
 
        onClicked:{//点击按钮事件;
            label.text = myQmlImp.getValue()}}}

在这里插入图片描述
到这里,我们就在 QML 中获取了 C++ 代码中的值。可能到这里还有老师感觉不太真实,那么我们就继续进行验证,我们的思路是这样的:

  1. 我们增加一个 TextFiled 用于输入我们想给 。
  2. 再增加一个 “设置” 按钮,将 1中输入的值,给C++ 中的 m_Value 设值。
  3. 然后我们再点击刚才的 “获取” 按钮,将 C++ 中的 m_Value 值读取并显示出来。 这时候我们需要对原来的QML进行改造,新增加一个输入框TextField ,进行数值输入; 还需要新增加一个“设置” 按钮,点击按钮将值赋给 C++ 中的 m_Value

main.qml

importQtQuick2.12importQtQuick.Window2.12importQtQuick.Controls2.3
 
Window {
    visible:true
    width:640
    height:480
    title:qsTr("Hello World") 
 
    Label{//Label用于显示获取C++的值
        id: label                  //显示控件,唯一标识ID:label
        text:""//初始化内容清空
 
        anchors.bottom: getBtn.top //显示控件的下方放到btn的上方
        anchors.left: getBtn.left  //显示控件的左方与btn的左侧对齐}
 
    Button{//Button 用于获取值
        id: getBtn                 //按钮控件,唯一标识ID:getBtn
        text:"获取"//按钮显示文字
        width:120//按钮宽度
        height:40//按钮高度
 
        anchors.centerIn: parent   //按钮放到窗口中心
 
        onClicked:{//点击按钮事件;
            label.text = myQmlImp.getValue()}}
 
    TextField{//文字输入控件
        id: textField               //唯一ID
        width: getBtn.width         //也可以直接设置成120
        height: getBtn.height       //也可以直接设置成40
 
        anchors.top: getBtn.bottom  //放到“获取”按钮下方10个像素
        anchors.topMargin:10
        anchors.left: getBtn.left   //与“获取”按钮左对齐}
 
    Button{
        id: setBtn
        text:"设置"
        width: textField.width      //可以设置成getBtn.width或者120
        height: textField.height    //可以设置成getBtn.height或者40
 
        anchors.top: textField.bottom
        anchors.left: textField.left
 
        onClicked:{
            var value = textField.text
            myQmlImp.setValue(value)}}}

在这里插入图片描述

■ 将C++对象注册到 QML中,在QML使用C++对象(Q_PROPERTY宏 属性绑定)

在 C++ 里定义了一个对象,然后将这个对象注册到 QML 里面。在 QML 里面访问的就是 C++ 定义的对象。

■ C++对象注册到元对象系统

QQmlApplicationEngine::rootContext()->setContextProperty()

■ Q_PROPERTY 宏

使用 Q_PROPERTY 定义交互的属性
Q_PROPERTY:用于声明属性的宏

■ 演示代码

person.h

#ifndefPERSON_H#definePERSON_H#include<QObject>classPerson:publicQObject{
    Q_OBJECT
    /* 使用 Q_PROPERTY 定义交互的属性 */Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)public:explicitPerson(QObject *parent =nullptr):QObject(parent),m_name(""),m_age(0){}/* 为属性提供 getter 和 setter 方法 */
    QString getName()const{return m_name;}voidsetName(const QString& name){ m_name = name; emit nameChanged();}intgetAge()const{return m_age;}voidsetAge(int age){ m_age = age; emit ageChanged();}

   signals:/* 信号与属性对应,通过信号通知其他对象属性的变化 */voidnameChanged();voidageChanged();private:
       QString m_name;int m_age;};#endif// PERSON_H

main.cpp

#include<QGuiApplication>#include<QQmlApplicationEngine>#include<QQmlContext>#include"person.h"intmain(int argc,char*argv[]){QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);// 创建Person对象
    Person person;
    QQmlApplicationEngine engine;/* 将Person对象作为QML上下文属性 */
    engine.rootContext()->setContextProperty("person",&person);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

importQtQuick2.12importQtQuick.Window2.12importQtQuick.Controls2.5

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

    Column {
        spacing:10

        TextField {
            placeholderText:"请输入姓名"
            text: person.name // 与Person对象的name属性绑定
            onTextChanged: person.name = text // 当文本改变时,更新Person对象的name属性}

        Slider {
            from:0
            to:100
            value: person.age // 与Person对象的age属性绑定
            onValueChanged: person.age = value // 当滑块值改变时,更新Person对象的age属性}

        Text {
            text:"姓名:"+ person.name
        }

        Text {
            text:"年龄:"+ person.age
        }}}

■ 将 C++类注册到 QML,并在QML声明一个对象并进行访问

■ C++类注册到qml中

qmlRegisterType 就是一个函数模板。将 C++ 的类型注册到 QML 系统中,并且带有版本号,方便版本管理。 我们就把main.cpp 中的函数改造一下:

■ 演示代码

#include<QGuiApplication>#include<QQmlApplicationEngine>#include<QQmlContext>#include"MyQmlClass.h"intmain(int argc,char*argv[]){QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 
    QGuiApplication app(argc, argv); 
    QQmlApplicationEngine engine;//    方式一:注册定义好的对象到 QML//    MyQmlClass myQmlImp;//    engine.rootContext()->setContextProperty("myQmlImp", &myQmlImp);//    方式二:注册类到 QML 对象qmlRegisterType<MyQmlClass>("com.company.myqmlclass",1,0,"MyQmlClass");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();}

其中:qmlRegisterType 模板函数中的 “com.company.myqmlclass” 为自定义的控件名称类似于C++中的库名称。我们在 QML 中需要 import 这个控件名, “MyQmlClass” 为 C++ 注册的类名, 1和0 为自定义版本号,方便版本管理。
在这里插入图片描述

importQtQuick2.12importQtQuick.Window2.12importQtQuick.Controls2.3importcom.company.myqmlclass1.0

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

    MyQmlClass{
        id: myQmlImp
    }

    Label{//Label用于显示获取C++的值
        id: label                  //显示控件,唯一标识ID:label
        text:""//初始化内容清空

        anchors.bottom: getBtn.top //显示控件的下方放到btn的上方
        anchors.left: getBtn.left  //显示控件的左方与btn的左侧对齐}

    Button{//Button 用于获取值
        id: getBtn                 //按钮控件,唯一标识ID:getBtn
        text:"获取"//按钮显示文字
        width:120//按钮宽度
        height:40//按钮高度

        anchors.centerIn: parent   //按钮放到窗口中心

        onClicked:{//点击按钮事件;
            label.text = myQmlImp.getValue()}}

    TextField{//文字输入控件
        id: textField               //唯一ID
        width: getBtn.width         //也可以直接设置成120
        height: getBtn.height       //也可以直接设置成40

        anchors.top: getBtn.bottom  //放到“获取”按钮下方10个像素
        anchors.topMargin:10
        anchors.left: getBtn.left   //与“获取”按钮左对齐}

    Button{
        id: setBtn
        text:"设置"
        width: textField.width      //可以设置成getBtn.width或者120
        height: textField.height    //可以设置成getBtn.height或者40

        anchors.top: textField.bottom
        anchors.left: textField.left

        onClicked:{
            var value = textField.text
            myQmlImp.setValue(value)}}}

■ 将 C++类注册到 QML,信号与槽进行交互

C++ 对象可以发出信号,而QML中的元素可以连接到这些信号上。这样,当C++ 对象的状态发生变化时,可以通过信号与槽机制将这些变化传递给QML界面。

■ C++类注册到qml中

qmlRegisterType 就是一个函数模板。将 C++ 的类型注册到 QML 系统中,并且带有版本号,方便版本管理。 我们就把main.cpp 中的函数改造一下:

■ 演示代码

myobject.h

#include<QObject>#include<QtDebug>classMyObject:publicQObject{
    Q_OBJECT 
public:explicitMyObject(QObject *parent =nullptr):QObject(parent){} 
signals:voidmySignal(QString message);public slots:voidmySlot(const QString& message){qDebug()<<"Received message from QML:"<< message; 
        emit mySignal("Hello from C++");}};

main.cpp

#include<QGuiApplication>#include<QQmlApplicationEngine>#include<QQmlContext>#include"myobject.h"intmain(int argc,char*argv[]){/* 启用Qt应用程序的高DPI缩放功能 */QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);/* 创建一个Qt应用程序的实例 */
    QGuiApplication app(argc, argv);/* 将自定义 C++ 类型注册到 QML 中的函数*/qmlRegisterType<MyObject>("com.example",1,0,"MyObject"); 
    
    QQmlApplicationEngine engine;const QUrl url(QStringLiteral("qrc:/main.qml"));/* 将 QQmlApplicationEngine 对象的 objectCreated 信号连接到一个 lambda 函数上 *//* lambda 函数用于在 QML 文件中的根对象被创建时进行处理,检查对象是否成功创建,如果创建失败则退出应用程序 */QObject::connect(&engine,&QQmlApplicationEngine::objectCreated,&app,[url](QObject *obj,const QUrl &objUrl){if(!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);/* 加载QML文件并显示用户界面 */
    engine.load(url);return app.exec();}

main.qml

importQtQuick2.12importQtQuick.Window2.12importQtQuick.Controls2.5importcom.example1.0
 
Window {
    visible:true
    width:480
    height:800
    title:qsTr("Hello World")/* 定义 sendToCpp 信号 */
    signal sendToCpp(string message)/* Connections 组件用于连接 myObject 的 onMySignal 信号 */
    Connections {
        target: myObject
        onMySignal: console.log("Received message from C++:", message)}
 
    MyObject {
        id: myObject
        /* 将 onMySignal 信号传递到 sendToCpp信号上,便于 QML 处理 */
        onMySignal:sendToCpp(message)}
 
    Button {
        text:"Send message to C++"
        anchors.centerIn: parent
        /* 单击按钮时,会将信号传递到 C++ 的 mySlot 槽上 */
        onClicked: myObject.mySlot("Hello from QML")}}

■ 将C++中的数据模型注册到C++中,qml进行访问 — 模型视图

模型视图(Model-View):可以使用C++ 中的数据模型(QStandardItemModel)来提供数据给QML界面。
QML中的视图元素(如ListView或GridView)可以使用这些模型来显示数据。

■ C++数据模型对象注册到元对象系统

QQmlApplicationEngine::rootContext()->setContextProperty()

■ 演示代码

mymodel.h

#ifndefMYMODEL_H#defineMYMODEL_H#include<QAbstractListModel>#include<QList>classMyModel:publicQAbstractListModel{
    Q_OBJECT 
public:explicitMyModel(QObject *parent =nullptr);enum{
        NameRole = Qt::UserRole +1,
        AgeRole,
        EmailRole
    };// 重写以下几个虚函数introwCount(const QModelIndex &parent =QModelIndex())constoverride;
    QVariant data(const QModelIndex &index,int role = Qt::DisplayRole)constoverride;
    QHash<int, QByteArray>roleNames()constoverride;private:structPerson{
        QString name;int age;
        QString email;}; 
    QList<Person> m_persons;};#endif// MYMODEL_H

mymodel.cpp

#include"mymodel.h"MyModel::MyModel(QObject *parent):QAbstractListModel(parent){// 初始化一些数据
    m_persons.append({"Alice",25,"[email protected]"});
    m_persons.append({"Bob",30,"[email protected]"});
    m_persons.append({"Charlie",35,"[email protected]"});}intMyModel::rowCount(const QModelIndex &parent)const{Q_UNUSED(parent);return m_persons.count();}
QVariant MyModel::data(const QModelIndex &index,int role)const{if(!index.isValid())returnQVariant();if(index.row()>= m_persons.count()|| index.row()<0)returnQVariant();const Person &person = m_persons[index.row()];if(role == NameRole)return person.name;elseif(role == AgeRole)return person.age;elseif(role == EmailRole)return person.email;returnQVariant();}
QHash<int, QByteArray>MyModel::roleNames()const{
    QHash<int, QByteArray> roles;
    roles[NameRole]="name";
    roles[AgeRole]="age";
    roles[EmailRole]="email";return roles;}

main.cpp

#include<QGuiApplication>#include<QQmlApplicationEngine>#include<QQmlContext>#include"mymodel.h"intmain(int argc,char*argv[]){/* 启用Qt应用程序的高DPI缩放功能 */QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);/* 创建一个Qt应用程序的实例 */
    QGuiApplication app(argc, argv);
 
    QQmlApplicationEngine engine;
 
    MyModel myModel;
    engine.rootContext()->setContextProperty("myModel",&myModel);const QUrl url(QStringLiteral("qrc:/main.qml"));/* 将 QQmlApplicationEngine 对象的 objectCreated 信号连接到一个 lambda 函数上 *//* lambda 函数用于在 QML 文件中的根对象被创建时进行处理,检查对象是否成功创建,如果创建失败则退出应用程序 */QObject::connect(&engine,&QQmlApplicationEngine::objectCreated,&app,[url](QObject *obj,const QUrl &objUrl){if(!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);/* 加载QML文件并显示用户界面 */
    engine.load(url);return app.exec();}

main.qml

importQtQuick2.12importQtQuick.Window2.12importQtQuick.Controls2.5
 
Window {
    visible:true
    width:480
    height:800
    title:qsTr("Hello World")
 
    ListView {
        anchors.fill: parent
        model: myModel
        delegate: Item {
            width: parent.width
            height:60
            Column {
                Text { text: name }
                Text { text: age }
                Text { text: email }}}}}

在这里插入图片描述

■ C++使用QML

■ 代码演示

main.qml

importQtQuick2.7importQtQuick.Window2.2
 
Item {
    id: root
    visible:true
    width:640
    height:480//title: qsTr("Hello World")
 
    property string msg:"I am QML Item"
    signal callCpp(string arg1, string arg2)
 
    MainForm {
        anchors.fill: parent
        mouseArea.onClicked:{
            Qt.quit();}}
 
    Rectangle {
        anchors.fill: parent
        color:"blue"
        objectName:"rect"}
 
    MouseArea {
        anchors.fill: parent
        onClicked:{
            console.log("onClicked, callCpp")
            root.callCpp(root.msg,"notify cpp")}}
 
    onHeightChanged:{
        console.log("onHeightChanged execute")}
    onWidthChanged:{
        console.log("onWidthChanged execute")}//QML中的方法可以被cpp调用,也可以作为槽函数
    function qmlFun(val_arg){
        console.log("qmlFun execute", val_arg,"return qmlFun_return_result")return"qmlFun_return_result"}//注意槽函数参数为var类型
    function invokeFromCpp(arg1, arg2){
        console.log("invokeFromCpp execute ", arg1, arg2)}}

cbusiness.h

#ifndefCBUSINESS_H#defineCBUSINESS_H#include<QObject>classCBusiness:publicQObject{
    Q_OBJECT
public:explicitCBusiness(QObject *parent =0);
 
signals:voidcallQml(const QVariant &arg1,const QVariant &arg2);public slots:voidinvokeFromQml(const QString &arg1,const QString &arg2);};#endif// CBUSINESS_H

cbusiness.cpp

#include"cbusiness.h"#include<QDebug>CBusiness::CBusiness(QObject *parent):QObject(parent){}voidCBusiness::invokeFromQml(const QString &arg1,const QString &arg2){qDebug()<<"CBusiness::"<< __FUNCTION__ << arg1 << arg2;qDebug()<<"CBusiness::"<< __FUNCTION__ <<" emit callQml";
    emit callQml("I am cpp","notify qml");}

main.cpp

#include<QGuiApplication>#include<QQmlApplicationEngine>#include<QQmlProperty>#include<QQuickView>#include<QQuickItem>#include<QMetaObject>#include<QDebug>#include"cbusiness.h"intmain(int argc,char*argv[]){QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
 
    QGuiApplication app(argc, argv);/*
    可以用QQmlComponent\QQuickView\QQuickWidget的C++代码加载QML文档
    当使用QQuickView时,qml的根不能是Window
    */
    QQuickView view(QUrl("qrc:/main.qml"));
    view.show();// 获取到qml根对象的指针
    QObject *qmlObj = view.rootObject();/*
        修改qml属性值的方法
        QObject::setProperty()  QQmlProperty  QMetaProperty::write()
        */// 通过QObject设置属性值qDebug()<<"cpp: "<<"set qml property height";QQmlProperty(qmlObj,"height").write(500);//qmlObj->setProperty("height",500);// 通过QObject获取属性值qDebug()<<"cpp: "<<"get qml property height"<< qmlObj->property("height").toDouble();// C++访问qml的其它属性qDebug()<<"cpp: "<<"get qml property msg"<< qmlObj->property("msg").toString();// 获取QQuickItem
    QQuickItem *item =qobject_cast<QQuickItem*>(qmlObj);// 通过QQuickItem设置属性值qDebug()<<"cpp: "<<"set qml property width";
    item->setWidth(300);// 通过QQuickItem获取属性值qDebug()<<"cpp: "<<"get qml property width"<< item->width();// 通过object name访问加载的QML对象// QObject::findChildren()可用于查找具有匹配object name属性的子项
    QObject *qmlRect = qmlObj->findChild<QObject*>("rect");if(qmlRect){qDebug()<<"cpp: "<<"get rect color"<< qmlRect->property("color");}// C++调用QML方法
    QVariant valReturn;
    QVariant valArg ="I am cpp";//Q_RETURN_ARG()和Q_Arg()参数必须制定为QVariant类型QMetaObject::invokeMethod(qmlObj,"qmlFun",Q_RETURN_ARG(QVariant,valReturn),Q_ARG(QVariant,valArg));qDebug()<<"cpp: "<<"QMetaObject::invokeMethod result"<< valReturn.toString();//qml函数中返回“ok”
 
 
    CBusiness cppObj;// cpp和qml信号与槽关联// qml信号绑订cpp的槽,用QString类型QObject::connect(qmlObj,SIGNAL(callCpp(QString, QString)),&cppObj,SLOT(invokeFromQml(QString, QString)));//关联cpp信号与qml槽// cpp的信号绑定qml槽,用QVariant类型QObject::connect(&cppObj,SIGNAL(callQml(QVariant, QVariant)), qmlObj,SLOT(invokeFromCpp(QVariant, QVariant)));return app.exec();}

测试结果
在这里插入图片描述
信号和槽的绑定在c++代码中完成,在c++中可以修改qml的属性,获取qml的属性,调用qml的方法,传递和获取参数均可以

鼠标点击
在这里插入图片描述

标签: qt

本文转载自: https://blog.csdn.net/sinat_23896491/article/details/135423953
版权归原作者 光芒Shine 所有, 如有侵权,请联系我们删除。

“【Qt- C++ & Qml 交互】”的评论:

还没有评论