zoukankan      html  css  js  c++  java
  • QML与C++混合编程

     之前写的文章都是一段一段的,现在整合起来,预估又是一段长臭文...

    零、前言

    1、先看一下相关类的继承关系:

    ①、视图关系

     ②、QML加载相关

     上方图参考:https://blog.csdn.net/qq_34139994/article/details/105195447

    2、关于Qt与QML的个人理解

    一切的一切,底层都是C++实现的,我们通过Qt封装好的C++类,如视图、引擎等,去加载QML文件,底层C++读取这些QML文本后,动态实例化相关对象,最终呈现在界面上。

     一、在Qt中加载QML的方式

    1、QQmlApplicationEngined搭配 Window

    2、QQuickView 搭配 Item

    3、QQuickWidget 加载 QML【Item】

    见之前文章:https://www.cnblogs.com/judes/p/15606042.html

    注意本文以Quick工程为例,非Widget工程,故选择的是前两种加载方式

    二、传递C++类型/对象给QML使用

    1、注册

     有非常多的注册模板函数可供使用,相关信息在手册里直接搜索qmlRegist..

     int qmlRegisterAnonymousType
     int qmlRegisterExtendedType
     int qmlRegisterExtendedUncreatableType
     int qmlRegisterInterface           //注册一个接口,不可被实例化,可以使用类型名引用【多态】
     void qmlRegisterModule
     int qmlRegisterRevision
     int qmlRegisterSingletonInstance
     int qmlRegisterSingletonType         //注册单例类型,可安全代替setContextProperty
     int qmlRegisterType              //注册一个可实例化对象,最常用
     int qmlRegisterTypeNotAvailable
     int qmlRegisterUncreatableMetaObject
     int qmlRegisterUncreatableType       //注册不可被实例化的对象,可以使用其中的枚举

    举例:创建一个C++的Udp类,并注册给QML用

    //cpp
    qmlRegisterType<Myudp>("Myudp.module",1,0,"Myudp");
    
    //qml
    import Myudp.module 1.0
    Myudp{
    id:udp
    }

     需要添加头文件:

    #include <QtQml/qqmlengine.h>

    2、设置上下文

    三种加载方式使用的工具是:

    QQmlApplicationEngined
    QQuickView
    QQuickWidget 

    它们都具有一个函数rootContext,返回QQmlContext;

    QQmlContext提供函数:setContextProperty【注册单个上下文属性】、setContextProperties【注册多个上下文属性】。

    这个上下文属性其实就是在C++里实例化了的对象,效果与上面的注册单例qmlRegisterSingletonType一致。

    举例:

    //C++
    Myudp udp;
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("udp",&udp);//注册对象
    
    //QML
    udp.initUdpSlot()

     注意:需要先设置上下文,再连接qml:

    三、封装C++对象

    上面只是QML里能够用简单访问到C++对象,是不足以支持实际项目开发的,故需要对C++对象进行封装。

    见之前的文章:https://www.cnblogs.com/judes/p/11242922.html

    Qt提供了这些宏用于封装:

    1、信号与槽
    C++类中的信号与槽都可以在QML中访问
    
    2、C++类的成员函数,Q_INVOKABLE
    Q_INVOKABLE void function();
    
    3、C++类的枚举,Q_ENUMS
    Q_ENUMS (enumName)
    
    4、C++的属性,Q_PROPERTY
    Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)

    1、例子:封装一个颜色生成器给QML使用,C++暴露属性、信号、槽、函数、枚举,QML使用【信号的使用有多种方式】

    ①、MyColor.h

    #ifndef MYCOLOR_H
    #define MYCOLOR_H
    
    #include <QObject>
    #include <QTimer>
    #include <QtQml/qqmlengine.h>//注册函数需要
    #include <QDebug>
    #include <QColor>
    
    class MyColor : public QObject
    {
        Q_OBJECT
        //枚举暴露
        Q_ENUMS(RECT_COLOR)
    
        //属性暴露,QML可直接使用属性[动态绑定、获取、设置,前提是匹配了对应函数]
        Q_PROPERTY(QString color READ getColor NOTIFY colorChanged)
        Q_PROPERTY(QString info READ getInfo WRITE setInfo)
    
        //属性相关
    public:
        explicit MyColor(QObject *parent = nullptr);
        inline QString getColor(){return m_cur_color;}
    
        inline QString getInfo(){return m_info;}
        inline void setInfo(QString info){m_info = info;qDebug()<<"info changed:"<<m_info;}
    
        inline void resetColor(){m_cur_index = 0;}
        inline static void RegisterType(){qmlRegisterType<MyColor>("MyColor.module",1,0,"MyColor");}
    
        //信号相关,QML可直接onSignal访问
    signals:
        void colorChanged();
    
        //公共函数相关
    public:
        Q_INVOKABLE inline void reSetColor(){m_cur_index = 0;}
    
        //槽函数相关,QML可直接访问,但是需要是public的,private访问会出错
    public slots:
        inline void addColor(){
            QColor clr(rand() % 256, rand() % 256, rand() % 256);
            m_colors.append(clr.name());
        }
    
        //枚举
    public:
        enum RECT_COLOR {
                RECT_COLOR_YELLOW,
                RECT_COLOR_RED,
                RECT_COLOR_BLUE,
                RECT_COLOR_ALL
            };
    
        //内部属性相关
    private:
        QString m_cur_color;       //当前的颜色
        int m_cur_index = 0;       //颜色序号
        QList<QString> m_colors;   //存储所有颜色列表
        QTimer m_timer;            //定时器修改当前颜色
    
        QString m_info;            //模拟一个变量,QML可修改
    };
    
    #endif // MYCOLOR_H

    ②、MyColor.cpp

    #include "mycolor.h"
    
    MyColor::MyColor(QObject *parent)
        : QObject{parent}
    {
        m_colors.append("red");
        m_colors.append("black");
        m_colors.append("green");
        m_colors.append("gray");
        m_colors.append("blue");
    
        QObject::connect(&m_timer, &QTimer::timeout, [this]{
            if(m_cur_index < m_colors.size())
            {
                m_cur_color = m_colors[m_cur_index++];
                emit colorChanged();
                qDebug()<<"C++ cur color:"<<m_cur_color;
            }
            else
            {
                m_cur_index = 0;
                m_cur_color = m_colors[m_cur_index++];
                emit colorChanged();
                qDebug()<<"C++ cur color:"<<m_cur_color;
            }
        });
        m_timer.start(1000);
    }

    ③、main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include "mycolor.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        MyColor::RegisterType();
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/use_mycolor.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }

    ④、qml

    import QtQuick 2.9
    import QtQuick.Window 2.2
    import QtQuick.Controls 2.3
    import MyColor.module 1.0
    
    Window {
        id: root
        visible: true
         770
        height: 200
        title: qsTr("Hello World")
    
        MyColor {
            id: c1
            onColorChanged: {
                r2.color = c1.color //①.直接在信号对象里获取信号参数
            }
        }
        Connections {
            target: c1
            function onColorChanged() {
                r3.color = c1.color //②.通过信号槽连接器捕捉信号
            }
        }
    
        Row {
            anchors.fill: parent
            spacing: 10
            Rectangle {
                id: r1
                 100
                height: 100
                color: c1.color //③.直接使用属性,进行属性绑定
            }
            Rectangle {
                id: r2
                 100
                height: 100
                color: "pink"
            }
            Rectangle {
                id: r3
                 100
                height: 100
                color: "pink"
            }
            Button {
                text: "add color"
                onClicked: {
                    c1.addColor()//④.直接调用C++对象的槽函数
                }
            }
            Button {
                text: "reset color"
                onClicked: {
                    c1.reSetColor()//⑤.调用函数[C++里需要标记上Q_INVOKABLE]
                }
            }
            Button {
                text: "set time to c++"
                onClicked: {
                    c1.info = Qt.formatDateTime(new Date(), "hh-mm-ss")//⑥.直接使用属性,进行属性设置
                }
            }
            Button {
                text: "get enum"
                onClicked: {
                    console.log(MyColor.RECT_COLOR_YELLOW)      //⑦.直接使用枚举
                }
            }
        }
    }

    第一个rect通过属性绑定来设置颜色;

    第二个rect通过在对象里捕捉信号,并设置颜色;

    第三个rect通过Connections连接器来设置颜色;【注意后两个rect的属性未绑定,相当于我们收到信号再主动去设置颜色】

    add color按钮直接调用C++的槽函数;

    reset color按钮调用C++的普通函数,此函数需要被Q_INVOKABLE标记;

    set time to c++按钮用于设置C++对象属性的值,这里设置的是当前时间,C++被设置后打印出来;

    get enum按钮获取C++类里定义的枚举值,这里是直接打印,这里的使用方式是:类名.枚举值【如果注册的是对象该怎么用呢?】。

    至此,这个例子是很全面的,描述了如何封装C++类,然后注册,然后QML如何访问。

    完整下载例子:https://download.csdn.net/download/m0_53292003/63241808

    2、例子2:封装UDP给QML使用

    见之前文章:https://www.cnblogs.com/judes/p/11241576.html

    四、QML里响应C++的信号

    //C++
    Class A{
      signals:
          void rcvData(QString str);     
    };
    //QML
    onRcvData:{
      //执行x如console.log(str);
    }

    见之前文章:https://www.cnblogs.com/judes/p/11243242.html

    五、C++访问QML对象的函数

    这种访问常用于第三种加载方式,通过获取QML根对象,再通过元对象的invokeMethod函数访问对象的函数。

    通过QMetaObject::invokeMethod,见之间文章:

     访问函数:https://www.cnblogs.com/judes/p/12939000.html

     传递List:https://www.cnblogs.com/judes/p/13029400.html

    六、C++捕捉QML的信号

    这种访问常用于第三种加载方式,通过获取QML根对象,再通过QObject::connect连接根对象的信号和C++的槽函数。

    #include <QQuickItem>
    //指定对象QObject* quitButton = root->findChild<QObject*>("quitButton");
    if (quitButton){
        QObject::connect(quitButton, SIGNAL(clicked()), &app, SLOT(quit()));
    }

    见之前文章:https://www.cnblogs.com/judes/p/11243242.html

    七、进阶之MVC编程

    MVC编程也是QML与C++的混合编程,通常C++提供Model也就是数据,QML提供View也就是视图。见之前文章:

    ListView:https://www.cnblogs.com/judes/p/13450897.html

    MVC:https://www.cnblogs.com/judes/p/15622972.html

  • 相关阅读:
    vue+node.js+webpack开发微信公众号功能填坑——组件按需引入
    myeclipse打开jsp页面慢或者卡死
    myeclipse自动添加注释
    解决java.lang.NoSuchMethodError:org.joda.time.DateTime.withTimeAtStartOfDay() Lorg/joda/time/DateTime
    Echarts柱状图实现不同颜色渐变色
    《Python学习手册 第五版》 -第38章 被管理的属性
    《Python学习手册 第五版》 -第37章 Unicode和字节串
    《Python学习手册 第五版》 -第36章 异常的设计
    《Python学习手册 第五版》 -第35章 异常对象
    《Python学习手册 第五版》 -第34章 异常编写细节
  • 原文地址:https://www.cnblogs.com/judes/p/15687205.html
Copyright © 2011-2022 走看看