• QML 与 C++ 交互


    前言

    文档如是说,QML旨在通过C ++代码轻松扩展。Qt QML模块中的类使QML对象能够从C ++加载和操作,QML引擎与Qt元对象系统集成的本质使得C ++功能可以直接从QML调用。这允许开发混合应用程序,这些应用程序是通过混合使用QML,JavaScript和C ++代码实现的。

    QML is designed to be easily extensible through C++ code. The classes in the Qt QML module enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's integration with Qt's meta object system enables C++ functionality to be invoked directly from QML. This allows the development of hybrid applications which are implemented with a mixture of QML, JavaScript and C++ code.

    除了从QML访问C ++功能的能力之外,Qt QML模块还提供了从C ++代码执行反向和操作QML对象的方法。

    下面会通过示例来讲解QML与C++的交互是如何实现的(感觉会有点长)。
    第一个例子:QML中创建C++对象

    文档如是说,使用C ++代码中定义的功能可以轻松扩展QML。由于QML引擎与Qt元对象系统的紧密集成,可以从QML代码访问由QObject派生的类适当公开的任何功能。这使得C ++类的属性和方法可以直接从QML访问,通常很少或无需修改。

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

        属性
        方法(需注册为public slots或是标记为Q_INVOKABLE)
        信号

    (此外,如果已使用Q_ENUMS声明枚举,则可以使用枚举。)

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

    代码示例有四个文件,QtQuick Empty工程的两个加自定义的Cpp类h和cpp文件,因为我把几种常用的方法都写出来了,所以看起来有点乱。

        //file CppObject.h
        #ifndef CPPOBJECT_H
        #define CPPOBJECT_H
         
        #include <QObject>
         
        //派生自QObject
        class CppObject : public QObject
        {
            Q_OBJECT
            //注册属性,使之可以在QML中访问--具体语法请参考其他资料
            Q_PROPERTY(QString name READ getName WRITE setName)
            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 yearChanged(int year);
         
        public slots:
            //public槽函数可以在QML中访问
            void cppSlotA();//一个无参槽函数
            void cppSlotB(const QString &str,int value);//一个带参数槽函数
         
        private:
            //类的属性
            QString myName;
            int myYear;
        };
         
        #endif // CPPOBJECT_H

    在头文件中,我定义了信号和public槽函数,以及Q_INVOKABLE宏标记的public函数,还通过Q_PROPERTY注册了两个属性,这些方法和属性之后都可以在QML中进行访问。

        //file CppObject.cpp
        #include "CppObject.h"
        #include <QDebug>
         
        CppObject::CppObject(QObject *parent)
            : QObject(parent),myName("none"),myYear(0)
        {
         
        }
         
        void CppObject::sendSignal()
        {
            //测试用,调用该函数后发送信号
            qDebug()<<"cpp sendSignal method";
            emit cppSignalA();
            emit cppSignalB(myName,myYear);
        }
         
        void CppObject::setName(const QString &name)
        {
            qDebug()<<"cpp setName"<<name;
            myName=name;
        }
         
        QString CppObject::getName() const
        {
            qDebug()<<"cpp getName";
            return myName;
        }
         
        void CppObject::setYear(int year)
        {
            qDebug()<<"cpp setYear"<<year;
            if(year!=myYear){
                qDebug()<<"cpp emit yearChanged";
                myYear=year;
                emit yearChanged(myYear);
            }
        }
         
        int CppObject::getYear() const
        {
            qDebug()<<"cpp getYear";
            return myYear;
        }
         
        void CppObject::cppSlotA()
        {
            qDebug()<<"cpp slot a";
        }
         
        void CppObject::cppSlotB(const QString &str, int value)
        {
            qDebug()<<"cpp slot b"<<str<<value;
        }

    为了测试方便,我给每个函数都加了一个打印语句,当调用sendSignal函数时将会emit两个信号,稍后会在QML中调用该函数。

        //file main.cpp
        #include <QGuiApplication>
        #include <QQmlApplicationEngine>
        #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;
            engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
            if (engine.rootObjects().isEmpty())
                return -1;
         
            return app.exec();
        }

    通过使用qmlRegisterType,将刚才定义的QObject派生类注册到QML中。

        //file main.qml
        import QtQuick 2.9
        import QtQuick.Window 2.2
        //引入我们注册的模块
        import MyCppObject 1.0
         
        Window {
            id: root
            visible: true
            640
            height: 480
            title: qsTr("Hello World")
         
            color:"green"
            signal qmlSignalA
            signal qmlSignalB(string str,int value)
         
            MouseArea{
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                //测试从点击开始
                //左键--Cpp发射信号
                //右键--Qml发射信号
                onClicked: {
                    if(mouse.button===Qt.LeftButton){
                        console.log('----clicked left button')
                        cpp_obj.name="gongjianbo"
                        cpp_obj.year=1992
                        cpp_obj.sendSignal() //调用Q_INVOKABLE宏标记的函数
                    }else{
                        console.log('----clicked right button')
                        root.qmlSignalA()
                        root.qmlSignalB('gongjianbo',1992)
                    }
                }
            }
         
            //作为一个QML对象
            CppObject{
                id:cpp_obj
                //也可以像原生QML对象一样操作
                property int counts: 0
         
                onYearChanged: {
                    counts++
                    console.log('qml name changed process')
                }
                onCountsChanged: {
                    console.log('qml counts changed process')
                }
            }
         
            Component.onCompleted: {
                //关联信号与信号处理函数的方式同QML中的类型
                //cpp object connect qml object
                cpp_obj.onCppSignalA.connect(function(){console.log('qml signal a process')})
                cpp_obj.onCppSignalB.connect(processB)
                //qml object connect cpp object
                root.onQmlSignalA.connect(cpp_obj.cppSlotA)
                root.onQmlSignalB.connect(cpp_obj.cppSlotB)
            }
         
            function processB(str,value){
                console.log('qml signal b process',str,value)
            }
        }

    注册之后就能直接在QML中使用刚才定义的C++类型了,并且可以像QML定义的类型一样进行操作,如信号槽关联、属性绑定等。

    这个示例很简单,点击鼠标左键调用CppObj的sendSignal函数来发送信号,QML处理;点击鼠标右键QML发送信号,CppObj处理,下面是操作结果:

        QML debugging is enabled. Only use this in a safe environment.
        qml: ----clicked left button
        cpp setName "gongjianbo"
        cpp setYear 1992
        cpp emit yearChanged
        qml: qml counts changed process
        qml: qml name changed process
        cpp sendSignal methodA
        qml: qml signal a process
        qml: qml signal b process gongjianbo 1992
        qml: ----clicked right button
        cpp slot a
        cpp slot b "gongjianbo" 1992

    可以看到QML成功的访问了CppObj的属性和方法,并能进行信号槽的关联。
    第二个例子:C++中加载QML对象

    文档如是说,所有QML对象类型都是源自QObject类型,无论它们是由引擎内部实现还是第三方定义。这意味着QML引擎可以使用Qt元对象系统动态实例化任何QML对象类型并检查创建的对象。

    这对于从C ++代码创建QML对象非常有用,无论是显示可以直观呈现的QML对象,还是将非可视QML对象数据集成到C ++应用程序中。一旦创建了QML对象,就可以从C ++中检查它,以便读取和写入属性,调用方法和接收信号通知。

    可以使用QQmlComponent或QQuickView来加载QML文档。QQmlComponent将QML文档作为为一个C++对象加载,然后可以从C++ 代码进行修改。QQuickView也可以这样做,但由于QQuickView是一个基于QWindow的派生类,加载的对象也将可视化显示,QQuickView通常用于将一个可视化的QML对象集成到应用程序的用户界面中。参见文档Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-interactqmlfromcpp.html

    下面通过代码来演示。

        //file main.qml
        import QtQuick 2.9
         
        Item{
            id: root
            100
            height: 100
            //自定义属性  --cpp可以访问
            property string msg: "gongjianbo1992"
            //自定义信号  --可以触发cpp槽函数
            signal qmlSendMsg(string msg)
         
            Rectangle {
                anchors.fill: parent
                color: "green"
                objectName: "rect"
            }
         
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    console.log("qml clicked, send qmlSendMsg signal")
                    root.qmlSendMsg(root.msg)
                }
            }
         
            onHeightChanged: console.log("qml height changed")
            onWidthChanged: console.log("qml width changed")
         
            //QML中的方法可以被cpp调用
            function qml_method(val_arg){
                console.log("qml method",val_arg)
                return "ok"
            }
        }

    在QML中我定义了一些属性和方法等,用于测试。
     

        //file CppObj.h
        #ifndef CPPOBJ_H
        #define CPPOBJ_H
         
        #include <QObject>
        #include <QDebug>
         
        class CppObj : public QObject
        {
            Q_OBJECT
        public:
            explicit CppObj(QObject *parent = Q_NULLPTR)
                :QObject(parent){}
         
        public slots:
            //槽函数 --用来接收qml的信号
            void cppRecvMsg(const QString &msg){
                qDebug()<<"cpp recv msg"<<msg;
            }
        };
         
        #endif // CPPOBJ_H

    Cpp中定义了一个槽函数,用来接收QML对象的信号。

        //file main.cpp
        #include <QGuiApplication>
        #include <QQmlProperty>
        #include <QQuickView>
        #include <QQuickItem>
        #include <QMetaObject>
        #include <QDebug>
         
        #include "CppObj.h"
         
        int main(int argc, char *argv[])
        {
            QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
         
            QGuiApplication app(argc, argv);
         
            //可以使用QQmlComponent或QQuickView的C++代码加载QML文档
            //QQuickView不能用Window做根元素
            QQuickView view(QUrl("qrc:/main.qml"));
            view.show();
         
            QObject *qmlObj=view.rootObject();
            /* 应该始终使用QObject::setProperty()、QQmlProperty
             * 或QMetaProperty::write()来改变QML的属性值,
             * 以确保QML引擎感知属性的变化。
             */
            //通过QObject设置属性值
            //qmlObj->setProperty("height",200);
            QQmlProperty(qmlObj,"height").write(200);
            //通过QObject获取属性值
            qDebug()<<"qml root height"<<qmlObj->property("height");
            //任何属性都可以通过C++访问
            qDebug()<<"qml property msg"<<qmlObj->property("msg");
         
            QQuickItem *item=qobject_cast<QQuickItem*>(qmlObj);
            //通过QQuickItem设置属性值
            item->setWidth(200);
            //通过QQuickItem获取属性值
            qDebug()<<"qml root width"<<item->width();
         
            //通过objectName访问加载的QML对象
            //QObject::findChildren()可用于查找具有匹配objectName属性的子项
            QObject *qmlRect=qmlObj->findChild<QObject*>("rect");
            if(qmlRect){
                qDebug()<<"qml rect color"<<qmlRect->property("color");
            }
         
            //调用QML方法
            QVariant val_return;       //返回值
            QVariant val_arg="=.=!";   //参数值
            //Q_RETURN_ARG()和Q_Arg()参数必须制定为QVariant类型
            QMetaObject::invokeMethod(qmlObj,
                                      "qml_method",
                                      Q_RETURN_ARG(QVariant,val_return),
                                      Q_ARG(QVariant,val_arg));
            qDebug()<<"qml method return value"<<val_return; //函数中返回“ok”
         
            //关联qml信号与cpp槽
            //如果信号参数为QML对象类型,信号用var参数类型,槽用QVariant类型接收
            CppObj cppObj;
            QObject::connect(qmlObj,SIGNAL(qmlSendMsg(QString)),
                             &cppObj,SLOT(cppRecvMsg(QString)));
         
            return app.exec();
        }

    然后就把文档中的东西测试了下,操作起来很简单。不想相对于QML中使用C++对象来说,感觉作用没那么大,毕竟QML访问C++也可以改变C++对象的状态,可能时我还没想到合适的应用场景。下面是我的测试输出结果:

        QML debugging is enabled. Only use this in a safe environment.
        qml: qml height changed
        qml root height QVariant(double, 200)
        qml property msg QVariant(QString, "gongjianbo1992")
        qml: qml width changed
        qml root width 200
        qml rect color QVariant(QColor, QColor(ARGB 1, 0, 0.501961, 0))
        qml: qml method =.=!
        qml method return value QVariant(QString, "ok")
        qml: qml clicked, send qmlSendMsg signal
        cpp recv msg "gongjianbo1992"

     以上两种方式应该就是最简单的QML与C++交互应用了,对照文档或是博客敲一遍代码可以很容易地理解。

    (完结)
    ---------------------  
    作者:龚建波  
    来源:CSDN  
    原文:https://blog.csdn.net/gongjianbo1992/article/details/87965925  
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    vm扩容
    手算二维离散傅里叶变换
    取消vim模式匹配的高亮
    DS DI ES SI等等
    int and Integer
    为什么超类能引用子类的对象
    voltile解析
    java集合
    疑问:无限定通配符
    layui table 导出
  • 原文地址:https://www.cnblogs.com/guozht/p/11269919.html
Copyright © 2020-2023  润新知