QML引用使用C++类和对象

文章目录

  • 前言
  • 实现C++类
    • 前提条件
    • Q_PROPERTY
    • 信号&槽
    • Q_INVOKABLE宏
    • Q_ENUM
  • 注册QML类型
    • 导入 C++ 注册的类型
  • 代码
  • 运行效果
  • 引用


前言

这里参考两篇博客,对上篇项目进行改造在QML中使用C++类和对象,达到混合编程的目的。

Qt Quick 可以快速构建 UI ,制作出具有动画、各种绚丽效果的 UI 。但Quick有其局限性,一些技术不可用或不可实现,这时候有必要使用与C++的混合编程。界面代码与逻辑代码分离,界面采用QML、JavaScript,逻辑采用C++。

QML 其实是对 JavaScript 的扩展,融合了 Qt Object 系统,它是一种新的解释型的语言。要在 QML 中访问 C++ 对象,那么必然要找到一种途径来在两个运行环境之间建立沟通桥梁,一般有两种方式。

  1. 在 C++ 中实现一个类,注册到 QML 环境中, QML 环境中使用该类型创建对象
  2. 在 C++ 中构造一个对象,将这个对象设置为 QML 的上下文属性,在 QML 环境中直接使用改属性

接下来首先要实现一个C++类。

实现C++类

前提条件

要想将一个类或对象导出到 QML 中,下列前提条件必须满足:

  1. 从 QObject 或 QObject 的派生类
  2. 继承 使用 Q_OBJECT 宏

与使用信号与槽的前提条件一样……没错,的确是一样的。这两个条件是为了让一个类能够进入 Qt 强大的元对象系统(meta-object system)中,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才具有了在 QML 中访问的基础条件。

接下来在原项目中创建一个基于QObejct的派生类:
QML引用使用C++类和对象_第1张图片
QML引用使用C++类和对象_第2张图片
这里设想时速表的速度值来自C++对象,因此创建了一个代表速度源的类,接下来看看如何将类的属性方法等暴露给QML

Q_PROPERTY

Q_PROPERTY 宏用来定义可通过元对象系统访问的属性,通过它定义的属性,可以在 QML 中访问、修改,也可以在属性变化时发射特定的信号。要想使用 Q_PROPERTY 宏,类必须是 QObject 的后裔,必须在类首使用 Q_OBJECT 宏。
Q_PROPERTY 宏的原型:

Q_PROPERTY(type name
           (READ getFunction [WRITE setFunction] |
            MEMBER memberName [(READ getFunction | WRITE setFunction)])
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [REVISION int]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [CONSTANT]
           [FINAL])

在实际使用中,很少能够用全 Q_PROPERTY 的所有选项,就往 QML 导出类这种场景来说,比较常用的是 READ / WRITE / NOTIFY 三个选项。一个简单的例子:
Q_PROPERTY(int x READ x)
定义了一个类型为 int 名为 x 的属性,通过方法 x() 来访问。

  • READ 标记,如果你没有为属性指定 MEMBER 标记,则 READ
    标记必不可少;声明一个读取属性的函数,该函数一般没有参数,返回定义的属性。
  • WRITE 标记,可选配置。声明一个设定属性的函数。它指定的函数,只能有一个与属性类型匹配的参数,必须返回 void 。
  • NOTIFY 标记,可选配置。给属性关联一个信号(该信号必须是已经在类中声明过的),当属性的值发生变化时就会触发该信号。信号的参数,一般就是定义的属性。

结合时速表的实例,定义一个速度值的属性:

 Q_PROPERTY(int speed READ speed WRITE setSpeed NOTIFY speedChanged)

信号&槽

只要是信号或者槽,都可以在 QML 中访问,你可以把 C++ 对象的信号连接到 QML 中定义的方法上,也可以把 QML 对象的信号连接到 C++ 对象的槽上,还可以直接调用 C++ 对象的槽或信号……

本例中定义speed属性改变的信号,另外定义两个槽函数,用于控制速度值自动更改的启停。

signals:
    void speedChanged(int value);

public slots:
    void start();
    void stop();

Q_INVOKABLE宏

在定义一个类的成员函数时使用 Q_INVOKABLE 宏来修饰,就可以让该方法被元对象系统调用。这个宏必须放在返回类型前面。
一旦使用 Q_INVOKABLE 将某个方法注册到元对象系统中,在 QML 中就可以用 O b j e c t . {Object}. Object.method来访问
本例定义了一个速度值变化周期的设定函数。

Q_INVOKABLE void setPeriod(int prd);

Q_ENUM

如果要导出的类定义了想在 QML 中使用枚举类型,可以使用 Q_ENUMS 宏将该枚举注册到元对象系统中。
一旦使用 Q_ENUMS 宏注册了你的枚举类型,在 QML 中就可以用 {CLASS_NAME}.{ENUM_VALUE} 的形式来访问。

注册QML类型

要注册一个 QML 类型,有多种方法可用,如 qmlRegisterSingletonType() 用来注册一个单例类型, qmlRegisterType() 注册一个非单例的类型, qmlRegisterTypeNotAvailable() 注册一个类型用来占位, qmlRegisterUncreatableType() 通常用来注册一个具有附加属性的附加类型。
这里使用qmlRegisterType:

qmlRegisterType<Speedsrc>("msh.qt.Speedsrc", 1, 0, "Speedsrc");

导入 C++ 注册的类型

一旦你在 C++ 中注册好了 QML 类型,就可以在 QML 文档中引入你注册的包,然后使用注册的类型。要引入包,使用 import 语句。比如要使用我们注册的 ColorMaker 类,可以在 QML 文档中加入下面的 import 语句:

import msh.qt.Speedsrc 1.0

代码

完整的的C++类代码

//speedsrc.h
#ifndef SPEEDSRC_H
#define SPEEDSRC_H

#include 

class Speedsrc : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int speed READ speed WRITE setSpeed NOTIFY speedChanged)

public:
    explicit Speedsrc(QObject *parent = nullptr);
    int speed();
    void setSpeed(int value);

    Q_INVOKABLE void setPeriod(int prd);

signals:
    void speedChanged(int value);

public slots:
    void start();
    void stop();

protected:
    void timerEvent(QTimerEvent *e);

private:
    int m_speed;
    int m_period;
    int m_timer;
};

#endif // SPEEDSRC_H
//speedsrc.cpp
#include "speedsrc.h"
#include 

Speedsrc::Speedsrc(QObject *parent) : QObject(parent)
{
    m_speed = 0;
    m_period = 1;
    m_timer = 0;
}

int Speedsrc::speed(){
    return m_speed;
}

void Speedsrc::setSpeed(int value){
    if(m_speed!=value)
        m_speed = value;
    emit speedChanged(value);
}

void Speedsrc::start(){
    if(m_timer == 0)
        m_timer = startTimer(m_period*50);
}

void Speedsrc::stop(){
    if(m_timer != 0)
    {
        killTimer(m_timer);
        m_timer = 0;
    }
}

void Speedsrc::setPeriod(int prd){

    m_period = prd;
    if(m_timer != 0)
    {
        killTimer(m_timer);
        m_timer = startTimer(m_period*50);
    }
}

void Speedsrc::timerEvent(QTimerEvent *e){
    int tmp = m_speed;
    static int step = 1;
    if(e->timerId()==m_timer)
    {
        if(tmp==200)
            step = -1;
        else if(tmp==0)
            step = 1;

        tmp+=step;
        setSpeed(tmp);
    }
    else
      {
          QObject::timerEvent(e);
      }

}

main.qml代码:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
import msh.qt.Speedsrc 1.0

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

    Speedsrc{
        id: speed_src
        speed: 0
    }

    Mymeter{
        id: meter_car
        x: 195
        y: 49
        width: 228
        height: 213
        dial_longNum: 10
        top_startangle: 140
        btm_startangle: 140
        btm_r: 80
        top_r: 80
        top_lineWidth: 15
        top_endangle: slider.value*1.3+140
        btm_lineWidth: 13
        btm_endangle: 400

        Text {
            id: element5
            x: 106
            y: -11
            width: 12
            height: 13
            text: qsTr("100")
            font.pixelSize: 12
        }

        Text {
            id: element6
            x: 156
            y: -1
            width: 13
            height: 14
            text: qsTr("120")
            font.pixelSize: 12
        }

        Text {
            id: element7
            x: 194
            y: 28
            width: 12
            height: 14
            text: qsTr("140")
            font.pixelSize: 12
        }

        Text {
            id: element9
            x: 218
            y: 77
            width: 12
            height: 14
            text: qsTr("160")
            font.pixelSize: 12
        }

        Text {
            id: element12
            x: 23
            y: 171
            width: 12
            height: 14
            text: qsTr("0")
            font.pixelSize: 12
        }

        Text {
            id: speed_unit
            x: 102
            y: 112
            width: 22
            height: 13
            text: qsTr("Km/h")
            font.pixelSize: 12
        }

        Text {
            id: speed
            x: 101
            y: 94
            width: 22
            height: 12
            text: slider.value
            font.bold: true
            font.pixelSize: 15
            horizontalAlignment: Text.AlignHCenter
        }

        Slider {
            id: slider
            x: 12
            y: 218
            stepSize: 1
            to: 200
            value: speed_src.speed

            onValueChanged: {
               if(value<60) {
                    speed.color = "green"
                }
                else if(value<120) {
                    speed.color = "orange"
                }
               else {
                   speed.color = "red"
               }
               speed_unit.color = speed.color
            }
        }

        Switch {
            id: swi
            x: 251
            y: -38
            width: 101
            height: 40
            text: qsTr("Switch")
            onClicked: {
            if(swi.position)
            {
                meter_car.top_lineWidth = meter_car.btm_lineWidth*0.6;
                speed_src.setPeriod(4);
             }

            else
            {

                meter_car.top_lineWidth = meter_car.btm_lineWidth;
                speed_src.setPeriod(1);
            }

        }
    }

    Text {
        id: element
        x: 15
        y: 37
        width: 12
        height: 14
        text: qsTr("60")
        font.pixelSize: 12
    }

    Text {
        id: element1
        x: -1
        y: 132
        width: 12
        height: 14
        text: qsTr("20")
        font.pixelSize: 12
    }

    Text {
        id: element2
        x: -5
        y: 82
        width: 12
        height: 14
        text: qsTr("40")
        font.pixelSize: 12
    }

    Text {
        id: element4
        x: 54
        y: 0
        width: 18
        height: 14
        text: qsTr("80")
        font.pixelSize: 12
    }

    Text {
        id: element10
        x: 218
        y: 127
        width: 12
        height: 14
        text: qsTr("180")
        font.pixelSize: 12
    }

    Text {
        id: element11
        x: 199
        y: 171
        width: 12
        height: 14
        text: qsTr("200")
        font.pixelSize: 12
    }
    }

    Button {
        id: start
        x: 181
        y: 337
        text: qsTr("Start")
        onClicked: {
            speed_src.start();
        }
    }

    Button {
        id: stop
        x: 322
        y: 338
        text: qsTr("Stop")
        onClicked: {
            speed_src.stop();
        }
    }

    Component.onCompleted: {

    }
}

运行效果

点击开始按钮后,速度值定时更新,并反馈到滑块和速度表上。Switch开关可以切换速度值更新周期,改变数值变化速度。
QML引用使用C++类和对象_第3张图片

引用

Qt Quick 之 QML 与 C++ 混合编程详解
QML入门----C++与QML交互快速应用

你可能感兴趣的:(QML,c++,qt)