zoukankan      html  css  js  c++  java
  • 29.qt quick-在QML中调用C++类

    • demo已放在群里,参考本文章一起学习, 效果更佳 

    1.Qml调用C++类

    Qt QML模块提供了一组API,用来将C++类扩展QML中。您可以编写扩展来添加自己的QML类型,扩展现有的Qt类型,或调用无法从普通QML代码访问的C/C++函数
    本章将学习如何使用C++类编写QML扩展,其中包括属性、QML function和属性绑定等
    为了方便大家理解,本章示例的函数实现能写在头文件,就写在头文件.

    2.创建QML
    将C++类扩展QML时,一般用来实现QML目前无法实现的功能,比如访问系统信息,文件信息等。
    本章demo是显示一个简单的饼图,创建一个C++类提供给QML使用

    这里导入一个"import Charts 1.0"模块,然后创建一个名为"PieChart"的QML类型,该类型具有两个属性:name和color

    import QtQuick.Window 2.12
    import Charts 1.0
    
    Window {
        visible: true
         640
        height: 480
        PieChart {
             100; height: 100
            name: "A simple pie chart"
            color: "red"
        }
    }

    要做到这一点,我们需要一个C++类,它封装了这个PieChart类型及其name和color两个属性。

    3.创建C++类

    由于QML大量使用了Qt的元对象系统,因此该类必须是:

    • 继承于QObject的派生类
    • 并且有Q_OBJECT宏

    以下是我们的饼图PieChart类,在piechart.h中定义:

    #include <QtQuick/QQuickPaintedItem>
    #include <QColor>
    #include <QPen>
    #include <QPainter>
    #include <QDebug>
    #include <QTimer>
    class PieChart : public QQuickPaintedItem
    {
        Q_OBJECT
        Q_PROPERTY(QString name READ name WRITE setName)
        Q_PROPERTY(QColor color READ color WRITE setColor)
    
    public:
        PieChart(QQuickItem *parent = 0);
    
        QString name() const { return m_name; }
        void setName(const QString &name) { m_name = name; }
    
        QColor color() const { return m_color; }
        void setColor(const QColor &color) { m_color = color; }
    
        void paint(QPainter *painter);
    
    private:
        QString m_name;
        QColor m_color;
    
    
    public slots:
        void onTimeout();
    
    };

    请注意,尽管color在QML中指定为字符串.比如"#00FF00",但它会自动转换为QColor对象,像“640x480”这样的字符串可以自动转换为QSize值。

    • 该类继承自QQuickPaintedItem,因为我们希望在使用QPainter API执行绘图操作时重写QQuickPaintedItem::paint()。
    • 如果类只是表示了某些数据类型,而不是实际需要显示的内容,它可以简单地从QObject继承。
    • 如果我们想创建一个不需要使用QPainter API执行绘图操作的可视化项,我们可以只对QQuickItem子类进行子类。

    Ps:

    • QQuickItem:  Qt Quick中的所有可视项都继承自QQuickItem。虽然QQuickItem没有视觉外观,但它定义了视觉项目中常见的所有属性,如x和y位置、宽度和高度、锚定和Key处理支持。
    • QQuickPaintedItem:继承自QQuickItem,并扩展了Qt中的QPainter API函数,使得QPainter将能够直接绘制到QML场景的纹理上。调用update()时可以重新绘制。在paint()中使用setAntaliasing()时可以设置抗锯齿渲染

    PieChart类使用Q_PROPERTY宏定义了两个属性name和color,并重写QQuickPaintedItem::paint()。

    3.1 Q_PROPERTY介绍
    Q_PROPERTY 宏定义属性的一些主要关键字的意义如下:

    • READ  指定一个读取属性值的函数,没有 MEMBER 关键字时必须设置 READ。
    • WRITE  指定一个设定属性值的函数,只读属性没有 WRITE 设置。
    • MEMBER  指定一个成员变量与属性关联,成为可读可写的属性,无需再设置 READ 和 WRITE。
    • RESET  是可选的,用于指定一个设置属性缺省值的函数。
    • NOTIFY  是可选的,用于设置一个信号,当属性值变化时发射此信号(在QML中经常用到,比如onXChanged)。
    • DESIGNABLE  表示属性是否在 Qt Designer 里可见,缺省为 true。
    • CONSTANT  表示属性值是一个常数,对于一个对象实例,READ 指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。
    • FINAL  表示所定义的属性不能被子类重载。

    在C++中属性的使用
    不管是否用 READ 和 WRITE 定义了接口函数,只要知道属性名称,就可以通过 QObject::property() 读取属性值,并通过 QObject::setProperty() 设置属性值。
    比如定义一个类:

    class MyObj : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString name READ name WRITE setName)
    
    public:
        MyObj(QQuickItem *parent = 0) { }
        
        QString name() const { return m_name; }
        void setName(const QString &name) { qDebug()<<name; m_name = name; }  // 添加了一个打印
    
    private:
        QString m_name; // 用来保存name属性的值
    };

    然后我们调用setProperty时:

    MyObj ct;
    ct.setProperty("name","1234"); // 将会调用setName()接口函数,并且打印"1234"

    在QML中属性的使用(在"5.属性绑定"会讲解)
    在QML中,属性就更加常见了,比如Rectangle的color属性,其实本质就是:

        Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    public:
        QColor color() const { return m_color; }
        void setColor(const QColor &color) { if (color == m_color) return; m_color = color; emit colorChanged(); }
    signals:
        void xChanged(const QString &name);
    private:
        QColor m_color;

    假如在c++类中自己更改属性时,并且该属性设置了NOTIFY关键字,那么必须更改后,主动emit来触发属性更改信号,比如:

    m_color = QColor::QColor(255, 0, 0, 255);
    emit colorChanged();

    3.2 piechart.cpp最终如下所示:

    #include "piechart.h"
    
    PieChart::PieChart(QQuickItem *parent)
    : QQuickPaintedItem(parent)
    {
    }
    
    void PieChart::paint(QPainter *painter)
    {
        QPen pen(m_color, 2);
        painter->setPen(pen);
        painter->setRenderHints(QPainter::Antialiasing, true);
        painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16);
    }

    4.在main.cpp中通过qmlRegisterXXX注册C++类到QML中
    我们已经创建好了C++类,剩下的就是注册到QML中即可大功告成了.注册函数是qmlRegisterType(),当然也可以通过qmlRegisterSingletonType()注册单例类(后面章节介绍).
    qmlRegisterType函数模版声明如下:

    template<typename T>
    int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
    // uri:  类似于java包名,比如"import QtQuick 2.12","QtQuick"就是包名,而2.12是versionMajor和versionMinor拼接的版本号
    // qmlName: 包名中的类型名称,比如Rectangle就是QtQuick包名中的其中一个类型名称

    main函数如下所示:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include "piechart.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<PieChart>("Charts", 1, 0, "PieChart"); // 注册C++类
    
    
        QQmlApplicationEngine engine;
        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();
    }

    运行效果如下所示:

    5.属性绑定
    属性绑定是QML的一个强大功能,它允许自动同步不同类型的值。当属性值更改时,它使用信号通知和更新其他类型的值。
    我们来创建两个PieChart图,名称分别为chartA和chartB,然后我们在chartB里进行color属性绑定"color: chartA.color".
    修改main.qml:

    import QtQuick 2.14
    import QtQuick.Window 2.12
    import Charts 1.0
    
    Window {
        visible: true
         640
        height: 480
        
        Row {
            PieChart {
                id: chartA
                 100; height: 100
                name: "A simple pie chart"
                color: "red"
            }
            PieChart {
                id: chartB
                 100; height: 100
                name: "A simple pie chart"
                color: chartA.color
            }
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: { chartA.color = "blue" }
        }
    
    }

    修改piechart.h:

    #include <QtQuick/QQuickPaintedItem>
    #include <QColor>
    #include <QPen>
    #include <QPainter>
    #include <QDebug>
    #include <QTimer>
    class PieChart : public QQuickPaintedItem
    {
        Q_OBJECT
        Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
        Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    
    public:
        PieChart(QQuickItem *parent = 0);
    
        QString name() const { return m_name; }
        void setName(const QString &name)
        {
            if (name != m_name) {
                m_name = name;
                emit nameChanged(name);
            }
        }
    
        QColor color() const { return m_color; }
        void setColor(const QColor &color)
        {
            if (color != m_color) {
                m_color = color;
                emit colorChanged(color);
                update();
            }
        }
        void paint(QPainter *painter);
    
    private:
        QString m_name;
        QColor m_color;
    
    signals:
        void nameChanged(const QString &name);
        void colorChanged(const QColor &color);
    
    public slots:
        void onTimeout();
    
    };

    这里我们在Q_PROPERTY()中添加了一个NOTIFY功能:

    • 属性绑定主要是靠的是属性中的NOTIFY功能,每当color值更改时,就会发出colorChanged信号。从而使得绑定的目标属性自动更新值.
    • 调用WRITE功能的函数时(比如setColor()),我们必须判断要设置的值是否与当前属性值相等,这样确保信号不会必要地发出,从而导致可能死循环的事情发生.

    当我们点击应用后,由于立即设置chartA.color = "blue",然后由于属性绑定,所以chartB也跟着改变了.最终两个chart都变成了蓝色:

    6.使用Q_INVOKABLE修饰函数提供给QML使用
    比如在QML中,我们想使用C++类的clearChart()函数来清除绘图时,那我们需要在C++类的clearChart()函数前面使用Q_INVOKABLE修饰来注册到元对象中(本质就是signal和slots).这样QML就可以像使用function那样调用该函数了.

    修改类头文件,添加clearChart():

    class PieChart : public QQuickPaintedItem
    {
        ...
    public:
        ...
        Q_INVOKABLE void clearChart();
    
    };

    在cpp文件中实现函数:

    void PieChart::clearChart()
    {
        setColor(QColor(Qt::transparent));
        update();
    }

    修改main.qml:

    Window {
        visible: true
         640
        height: 480
        
        Row {
            PieChart {
                id: chartA
                 100; height: 100
                name: "A simple pie chart"
                color: "red"
            }
            PieChart {
                id: chartB
                 100; height: 100
                name: "A simple pie chart"
                color: "blue"
            }
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: { chartA.clearChart();}
        }
    }

    当我们点击应用后,就会调用chartA.clearChart()方法,从而清除chartA:

    7.信号、槽函数提供给QML使用

    上一节我们讲过使用Q_INVOKABLE修饰来注册到元对象中其实本质就是signal和slots.这是因为:

    所以不管用Q_INVOKABLE,还是signalslots修饰,最终都会变成QT_ANNOTATE_FUNCTION(...)

    修改piechart.h,添加一个clearChart2槽函数:

    class PieChart : public QQuickPaintedItem
    {
        ...
    public slots:
        void clearChart2()
        {
            setColor(QColor(Qt::transparent));
            update();
        }
        ...
    }; 

    修改main.qml:

    MouseArea {
        anchors.fill: parent
        onClicked: { chartA.clearChart2();}
    }

    发现,最终效果和调用chartA.clearChart()的效果一样.


    8.使用Q_ENUM()将枚举提供给QML使用
    在C++类中,我们可以通过Q_ENUM()将C++类的枚举类型注册到元对象中,修改头文件如下所示:

    class PieChart : public QQuickPaintedItem
    {
        ...
    public :
        enum Priority {
            High,
            Low,
            VeryHigh,
            VeryLow
        };
        Q_ENUM(Priority)
    
        Q_INVOKABLE Priority setPriority(Priority value)
        {
            qDebug()<<value;
        }
        ...
    };

    在main.qml中添加:

    Component.onCompleted: {
        chartA.setPriority(chartA.VeryHigh);
    }

    运行效果如下所示:

    未完待续,下章学习: 30.qt quick-使用qmlRegisterSingletonType注册单例类给QML使用


    人间有真情,人间有真爱。

    如果您喜欢这里,感觉对你有帮助,并且有多余的软妹币的话,不妨投个食吧,赞赏的时候,留下美句和你的博客地址哦~   戳这里看谁投食了


查看全文
  • 相关阅读:
    前端面试常考知识点---CSS
    vue中的适配:px2rem
    判断DOM元素是否出现再浏览器窗口中
    前端构建:3类13种热门工具的选型参考
    webpack4 中的最新 React全家桶实战使用配置指南!
    [C++] 自动关闭右下角弹窗
    Java RandomAccessFile用法(转载)
    Java Annotation详解(二): 反射和Annotation
    Java Annotation详解(一): 理解和使用Annotation
    Java反射机制(五):使用反射增强简单工厂设计模式
  • 原文地址:https://www.cnblogs.com/lifexy/p/14901056.html
  • Copyright © 2011-2022 走看看