目录
一、前言
二、C++与QML集成的基础
2.1 语言特征
2.2 可集成的前提条件
2.3 基础数据类型
2.4 自定义数据类型
三、实例讲解
3.1 QML获取C++类的自定义结构体数据
3.1.1 结构体类型
3.1.2 配置数据类
3.1.3 配置信息管理类
3.1.4 向QML元对象系统注册自定义类
3.1.5 QML调用C++函数后得到自定义结构体数据
3.2 C++类接收QML传递的自定义数据
3.2.1 传递QML数组保存自定义数据
3.2.2 C++类解析QML数组数据
总结
界面代码与逻辑代码分离困扰过许多软件开发人员,使用C++与QML混合编程在一定程序上解决上这个问题,但由于它们之间的数据类型不完全兼容,对于一些复杂数据的传递(例如:结构体数据),则需要重点关注和解决,本文结合自己的实践经验分享出解决方案。
C++的三个主要特征:
- 封装;
- 继承;
- 多态;
QML的两个主要特征:
- 描述用户界面的一种陈述性语言;
- 支持javacript形式的编程控制;
Qt元对象系统是Qt 类库独有的功能,是Qt对标准C++的扩展,它提供了三个功能:
- 对象之间通信机制(信号与槽);
- 运行时类型信息;
- 动态属性;
实现元对象,要满足三个条件:
- 该类必须继承自QObject类;
- 类的私有区域声明Q_OBJECT宏,用于启动元对象特性,使用动态特性,信号和槽;
- 元对象编译器(moc)为每个QObject子类,提供了实现元对象特性必须的代码 ;
由于QML引擎与Qt元对象系统的集成,可以从QML中访问任何从QObject继承的类的属性、方法和信号,C++代码既可以在应用中集成,也可以在插件中集成。
QML访问C++数据主要有三种方法:
- 将C++ 类的属性暴露给QML;
- 从C++ 定义QML类型;
- 用Context属性在QML中嵌入C++对象;
C++和QML能自动转化部分常用的基础数据类型大约有16种,清单如下:
QT | QML |
---|---|
bool | bool |
unsigned int, int | int |
double | double |
float, qreal | real |
QString | string |
QUrl | url |
QColor | color |
QFont | font |
QDateTime | date |
QPoint, QPointF | point |
QSize, QSizeF | size |
QRect, QRectF | rect |
QMatrix4x4 | matrix4x4 |
QQuaternion | quaternion |
QVector2D, QVector3D, QVector4D | vector2d, vector3d, vector4d |
Q_ENUM() 或 Q_ENUMS() | enumeration |
16种基础数据类型以外的其它数据类型是QML所无法识别的,可将它定义为复杂数据类型,结构体数据类型属于复杂数据类型中的一种。由于QObject子类都可以注册为QML对象类型,所以构造结构体对应的自定义类来与QML交互是可行的。
本人参与的开源项目iCupBox就应用了C++与QML混合编程,使用自定义结构体数据来代表配置信息项,通过自定义结构体数据的传递,实现数据配置的加载和保存。
根据喝水提醒功能业务的需求,分析得到要配置的自定义结构体“ST_CFG_WATER_CLOCK”。
typedef struct ST_CFG_WATER_CLOCK {
int nWaitingInterval;
int nReminderTimes;
int nTwinkle;
ST_CFG_BASIC() {
nWaitingInterval = 0;
nReminderTimes = 0;
nTwinkle = 0;
}
}CFG_WATER_CLOCK;
声明喝水提醒功能类(config_ini.h文件)
设置Q_PROPERTY宏,C++中的变量与QML的属性建立绑定。
class WaterClock : public QObject
{
Q_OBJECT
Q_PROPERTY(int waitingInterval READ getWaitingInterval WRITE setWaitingInterval)
Q_PROPERTY(int reminderTimes READ getReminderTimes WRITE setReminderTimes)
Q_PROPERTY(int twinkle READ getTwinkle WRITE setTwinkle)
public:
explicit WaterClock(QObject *parent = 0);
~WaterClock();
int getWaitingInterval() const;
int getReminderTimes() const;
int getTwinkle() const;
void setWaitingInterval(int);
void setReminderTimes(int);
void setTwinkle(int);
private:
int m_nWaitingInterval;
int m_nReminderTimes;
int m_nTwinkle;
};
实现喝水提醒功能类(config_ini.cpp文件)
#include "config_ini.h"
WaterClock::WaterClock(QObject *parent)
{
}
WaterClock::~WaterClock()
{
}
int WaterClock::getWaitingInterval() const
{
return m_nWaitingInterval;
}
int WaterClock::getReminderTimes() const
{
return m_nReminderTimes;
}
int WaterClock::getTwinkle() const
{
return m_nTwinkle;
}
void WaterClock::setWaitingInterval(int nWaitingInterval)
{
m_nWaitingInterval = nWaitingInterval;
}
void WaterClock::setReminderTimes(int nReminderTimes)
{
m_nReminderTimes = nReminderTimes;
}
void WaterClock::setTwinkle(int nTwinkle)
{
m_nTwinkle = nTwinkle;
}
声明配置信息管理类(config_mgr.h)
设置Q_INVOKABLE宏,C++中的函数允许被QML调用。
#ifndef CONFIG_MGR_H
#define CONFIG_MGR_H
#include
#include "config_ini.h"
class ConfigMgr : public QQuickItem
{
Q_OBJECT
public:
explicit ConfigMgr(QQuickItem *parent = 0);
~ConfigMgr() override;
public:
Q_INVOKABLE WaterClock *loadWaterClockInfo();
Q_INVOKABLE bool saveWaterClockInfo(const QVariantList &lstWaterClock);
};
#endif // CONFIG_MGR_H
实现配置信息管理类(config_mgr.cpp)
#include "config_mgr.h"
#include
ConfigMgr::ConfigMgr(QQuickItem *parent):
QQuickItem(parent)
{
// By default, QQuickItem does not draw anything. If you subclass
// QQuickItem to create a visual item, you will need to uncomment the
// following line and re-implement updatePaintNode()
// setFlag(ItemHasContents, true);
Q_ASSERT(NULL != ConfigINI::GetInstance(this));
qDebug() << Q_FUNC_INFO;
}
ConfigMgr::~ConfigMgr()
{
qDebug() << Q_FUNC_INFO;
}
WaterClock *ConfigMgr::loadWaterClockInfo()
{
qDebug() << Q_FUNC_INFO;
// Read data from INI
ST_CFG_WATER_CLOCK stCfgWaterClock;
ConfigINI::GetInstance(this)->ReadIni(stCfgWaterClock);
// Assign value from structure to object
WaterClock *pWaterClock = new WaterClock();
pWaterClock->setWaitingInterval(stCfgWaterClock.nWaitingInterval);
pWaterClock->setReminderTimes(stCfgWaterClock.nReminderTimes);
pWaterClock->setTwinkle(stCfgWaterClock.nTwinkle);
return pWaterClock;
}
bool ConfigMgr::saveWaterClockInfo(const QVariantList &lstWaterClock)
{
qDebug() << Q_FUNC_INFO << lstWaterClock.size();
if(3 != lstWaterClock.size()) return false;
// Assign value from variant to structure
ST_CFG_WATER_CLOCK stCfgWaterClock;
stCfgWaterClock.nWaitingInterval = lstWaterClock[0].toInt();
stCfgWaterClock.nReminderTimes = lstWaterClock[1].toInt();
stCfgWaterClock.nTwinkle = lstWaterClock[2].toInt();
// Write data into INI
ConfigINI::GetInstance(this)->WriteIni(stCfgWaterClock);
return true;
}
使用qmlRegisterType,将自定义的QObject派生类注册到QML,它是连接C++和QML的工具。
#include "ipluginsmgr_plugin.h"
#include "config/config_mgr.h"
#include
void IPluginsMgrPlugin::registerTypes(const char *uri)
{
// @uri com.mycompany.qmlcomponents
// Register class types to QML
qmlRegisterType(uri, 1, 0, "WaterClock");
qmlRegisterType(uri, 1, 0, "ConfigMgr");
}
QML及C++插件项目的文件分布如下:
main.qml文件
import QtQuick 2.5
import QtQuick.Window 2.2
import com.mycompany.qmlcomponents 1.0
Window {
id: wndRoot
visible: true
width: 1000
height: 800
title: qsTr("iCupBox v1.0.0")
MainForm {
anchors.fill: parent
}
ConfigMgr {
id: cppConfigMgr
}
}
WaterClockView.qml文件
Component.onCompleted: {
// Load default parameter data
var pWaterClock = cppConfigMgr.loadWaterClockInfo()
console.log("cmb_waiting_time::onCompleted " + pWaterClock.waitingInterval, pWaterClock.reminderTimes, pWaterClock.twinkle)
cmb_waiting_time.currentIndex = pWaterClock.waitingInterval;
cmb_reminder_times.currentIndex = pWaterClock.reminderTimes;
cmb_twinkle.currentIndex = pWaterClock.twinkle;
}
在QML中的使用信号-槽来收集页面上的配置信息数据后,使用var数据的形式暂时存储,再将其传递给C++类。
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Item {
signal currentIndexChanged()
function onHandleCurrentIndexChanged() {
// Get the change value of the index
var arrWaterClock = []
arrWaterClock.push(cmb_waiting_time.currentIndex)
arrWaterClock.push(cmb_reminder_times.currentIndex)
arrWaterClock.push(cmb_twinkle.currentIndex)
// Modify default parameter data
var result = cppConfigMgr.saveWaterClockInfo(arrWaterClock)
console.log("onHandleUpdateWaterClock ", result)
}
...
ComboBox {
id: cmb_waiting_time
height: 25
Layout.minimumWidth: 100
model:[qsTr("25 minutes"), qsTr("50 minutes"), qsTr("75 minutes"), qsTr("100 minutes")]
Component.onCompleted: {
currentIndexChanged.connect(onHandleCurrentIndexChanged)
}
onActivated: {
currentIndex = index
console.log("cmb_waiting_time::onActivated", currentIndex)
// The index is changed, and the default configuration is updated
currentIndexChanged()
}
}
}
在config_mgr.cpp类中处理QML数据。
bool ConfigMgr::saveWaterClockInfo(const QVariantList &lstWaterClock)
{
qDebug() << Q_FUNC_INFO << lstWaterClock.size();
if(3 != lstWaterClock.size()) return false;
// Assign value from variant to structure
ST_CFG_WATER_CLOCK stCfgWaterClock;
stCfgWaterClock.nWaitingInterval = lstWaterClock[0].toInt();
stCfgWaterClock.nReminderTimes = lstWaterClock[1].toInt();
stCfgWaterClock.nTwinkle = lstWaterClock[2].toInt();
// Write data into INI
ConfigINI::GetInstance(this)->WriteIni(stCfgWaterClock);
return true;
}
C++与QML混合编程就是优势互补的编程策略,使用QML高效便捷地构建UI,使用C++实现业务逻辑和复杂算法。