这里参考两篇博客,对上篇项目进行改造在QML中使用C++类和对象,达到混合编程的目的。
Qt Quick 可以快速构建 UI ,制作出具有动画、各种绚丽效果的 UI 。但Quick有其局限性,一些技术不可用或不可实现,这时候有必要使用与C++的混合编程。界面代码与逻辑代码分离,界面采用QML、JavaScript,逻辑采用C++。
QML 其实是对 JavaScript 的扩展,融合了 Qt Object 系统,它是一种新的解释型的语言。要在 QML 中访问 C++ 对象,那么必然要找到一种途径来在两个运行环境之间建立沟通桥梁,一般有两种方式。
接下来首先要实现一个C++类。
要想将一个类或对象导出到 QML 中,下列前提条件必须满足:
与使用信号与槽的前提条件一样……没错,的确是一样的。这两个条件是为了让一个类能够进入 Qt 强大的元对象系统(meta-object system)中,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才具有了在 QML 中访问的基础条件。
接下来在原项目中创建一个基于QObejct的派生类:
这里设想时速表的速度值来自C++对象,因此创建了一个代表速度源的类,接下来看看如何将类的属性方法等暴露给QML
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() 来访问。
结合时速表的实例,定义一个速度值的属性:
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 将某个方法注册到元对象系统中,在 QML 中就可以用 O b j e c t . {Object}. Object.method来访问
本例定义了一个速度值变化周期的设定函数。
Q_INVOKABLE void setPeriod(int prd);
如果要导出的类定义了想在 QML 中使用枚举类型,可以使用 Q_ENUMS 宏将该枚举注册到元对象系统中。
一旦使用 Q_ENUMS 宏注册了你的枚举类型,在 QML 中就可以用 {CLASS_NAME}.{ENUM_VALUE} 的形式来访问。
要注册一个 QML 类型,有多种方法可用,如 qmlRegisterSingletonType() 用来注册一个单例类型, qmlRegisterType() 注册一个非单例的类型, qmlRegisterTypeNotAvailable() 注册一个类型用来占位, qmlRegisterUncreatableType() 通常用来注册一个具有附加属性的附加类型。
这里使用qmlRegisterType:
qmlRegisterType<Speedsrc>("msh.qt.Speedsrc", 1, 0, "Speedsrc");
一旦你在 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开关可以切换速度值更新周期,改变数值变化速度。
Qt Quick 之 QML 与 C++ 混合编程详解
QML入门----C++与QML交互快速应用