C++与QML混合编程技术(传递自定义数据类型)

目录

一、前言

二、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集成的基础

2.1 语言特征

 C++的三个主要特征: 

  1. 封装;
  2. 继承;
  3. 多态;

 QML的两个主要特征:

  1. 描述用户界面的一种陈述性语言;
  2. 支持javacript形式的编程控制;

2.2 可集成的前提条件

 Qt元对象系统是Qt 类库独有的功能,是Qt对标准C++的扩展,它提供了三个功能:

  1. 对象之间通信机制(信号与槽);
  2. 运行时类型信息;
  3. 动态属性;

实现元对象,要满足三个条件:

  1. 该类必须继承自QObject类;
  2. 类的私有区域声明Q_OBJECT宏,用于启动元对象特性,使用动态特性,信号和槽;
  3. 元对象编译器(moc)为每个QObject子类,提供了实现元对象特性必须的代码 ;

由于QML引擎与Qt元对象系统的集成,可以从QML中访问任何从QObject继承的类的属性、方法和信号,C++代码既可以在应用中集成,也可以在插件中集成。

QML访问C++数据主要有三种方法:

  1. 将C++ 类的属性暴露给QML;
  2. 从C++ 定义QML类型;
  3. 用Context属性在QML中嵌入C++对象;

2.3 基础数据类型

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

2.4 自定义数据类型

16种基础数据类型以外的其它数据类型是QML所无法识别的,可将它定义为复杂数据类型,结构体数据类型属于复杂数据类型中的一种。由于QObject子类都可以注册为QML对象类型,所以构造结构体对应的自定义类来与QML交互是可行的。

三、实例讲解

本人参与的开源项目iCupBox就应用了C++与QML混合编程,使用自定义结构体数据来代表配置信息项,通过自定义结构体数据的传递,实现数据配置的加载和保存。

C++与QML混合编程技术(传递自定义数据类型)_第1张图片

3.1 QML获取C++类的自定义结构体数据

3.1.1 结构体类型

根据喝水提醒功能业务的需求,分析得到要配置的自定义结构体“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;

3.1.2 配置数据类

 声明喝水提醒功能类(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;
}

3.1.3 配置信息管理类

声明配置信息管理类(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;
}

3.1.4 向QML元对象系统注册自定义类

使用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");
}

3.1.5 QML调用C++函数后得到自定义结构体数据

QML及C++插件项目的文件分布如下:

C++与QML混合编程技术(传递自定义数据类型)_第2张图片

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;
        }

 3.2 C++类接收QML传递的自定义数据

3.2.1 传递QML数组保存自定义数据

在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()
            }
        }
}

3.2.2 C++类解析QML数组数据

在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++实现业务逻辑和复杂算法。

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