QML中一种模型基类

目录

  • 引言
  • 核心代码
    • 快捷宏
    • 序列化、反序列化
    • 属性变化信号
  • 完整代码

引言

此处说的模型和之前所说的QStandardItemModel并不是一个概念,是一个更广泛的概念,在QML中model/view的实现方式并不仅限于QStandardItemModel这类,也可以是继承QObject使用属性系统的简单类,将界面上使用到的属性映射到该类上即可,如一个文件导出弹窗,界面上由两个编辑栏,一个用于输入文件名fileName,另一个用于输入文件路径filePath,而这两个则对应模型类内的两个属性,通过属性绑定显示在QML中UI组件。

而这类界面模型类,通常需要有保存成配置文件的功能,能够快速将原有属性进行序列化和反序列化,此处我们选择Json,因为Json在QML中更为通用。该类的特点总结如下:

  1. 使用属性系统进行映射
  2. 提供FromJson和ToJson接口
  3. 提供统一的属性变化信号,方便触发文件存储

核心代码

快捷宏

用过Qt属性系统的应该都感受到较为范围,需要有属性名、读函数、写函数以及变化信号,而上述模型类中的属性通常是非常多的,这里会增加编程负担,因此增加一个快捷宏,将定义封装成宏使用,如下所示:

#define ADD_PROPERTY(type, name)                                          \
  Q_PROPERTY(type name READ name WRITE name##Config NOTIFY name##Changed) \
 public:                                                                  \
  type name();                                                            \
  void name##Config(type value);                                          \
  Q_SIGNAL                                                                \
  void name##Changed();                                                   \
                                                                          \
 private:                                                                 \
  type name##_;

这里没有把函数的定义包裹在宏中,因为那样后续就没办法调试,会增加维护成本。

序列化、反序列化

前文提到,该模型类使用的是属性系统,那么在序列化时只需要遍历所有的属性,将它们组装为Json即可,如下所示:


bool JsonInfoBase::FromJson(const QJsonObject &json_object)
{
    for(const auto &name : PropertyNames()){
        if(!json_object.contains(name)){
            qDebug()<<__FUNCTION__<<"error:"<<name;
            continue;
        }
        setProperty(name, json_object.value(name));
    }

    return true;
}
QJsonObject JsonInfoBase::ToJson() const
{
    QJsonObject json_object;
    for(const auto &name : PropertyNames()){
        if(!property(name).isValid() || property(name).isNull()){
            continue;
        }
        json_object.insert(name, QJsonValue::fromVariant(property(name)));
    }
    return json_object;
}
QList<QByteArray> JsonInfoBase::PropertyNames() const
{
    QList<QByteArray> property_names;
    auto meta_object = this->metaObject();
    for (int i=0; i < meta_object->propertyCount(); i++){
        property_names << meta_object->property(i).name();
    }
    property_names.removeAll("objectName");// 移除自带的
    return  property_names;
}

属性变化信号

解决了序列化和反序列化的问题,还需要解决何时触发数据的存储,当然是属性发生变化就触发存储,可以逐个属性变化信号进行关联,但这就意味着后续每增加一个属性就需要关联一次,很容易遗忘,增加维护难度,需要一种一劳永逸的方式,自动进行信号关联,这里同样使用到Qt的反射机制,在运行时通过属性名称获得对于属性的变化信号函数,将其关联至同一个信号,代码如下:

void JsonInfoBase::ObservePropertyChanged(const QStringList &ignore_propertys)
{
    auto interested_propertys = PropertyNames();

    auto meta_object = this->metaObject();
    for (int i=0; i < meta_object->propertyCount(); i++){
        QMetaProperty meta_property = meta_object->property(i);
        if(!interested_propertys.contains(meta_property.name())){
            continue;
        }

        if(ignore_propertys.contains(meta_property.name())){
            continue;
        }

        if(meta_property.hasNotifySignal()){
            const QMetaMethod notify_signal = meta_property.notifySignal();
            const QMetaMethod property_changed = meta_object->method(meta_object->indexOfMethod("propertyChanged()"));
            connect(this, notify_signal, this, property_changed, Qt::UniqueConnection);
        }
    }
}

完整代码

#ifndef JSONINFOBASE_H
#define JSONINFOBASE_H

#include 
#include 

#define ADD_PROPERTY(type,name) \
    Q_PROPERTY(type name READ name WRITE name##Config NOTIFY name##Changed) \
public: \
    type name(); \
    bool name##Config(type value); \
Q_SIGNAL \
    void name##Changed(); \
private: \
    type name##_;

class JsonInfoBase : public QObject
{
    Q_OBJECT

public:
    explicit JsonInfoBase(QObject *parent = nullptr);

public:
    // json_object转换为类的属性
    virtual bool FromJson(const QJsonObject &json_object);

    // 属性转换为json_object
    virtual QJsonObject ToJson();

    void ObservePropertyChanged(const QStringList &ignore_propertys = QStringList());

Q_SIGNALS:
    // 使用前需要在子类构造函数中调用ObservePropertyChanged
    void propertyChanged();

protected:
    QList<QByteArray> PropertyNames() const;
};

#endif // JSONINFOBASE_H

#include "json_info_base.h"
#include 

JsonInfoBase::JsonInfoBase(QObject *parent)
    : QObject{parent}
{

}

bool JsonInfoBase::FromJson(const QJsonObject &json_object)
{
    foreach (const QByteArray &name, PropertyNames()){
        if(!json_object.contains(name)){
            qDebug()<<__FUNCTION__<<"error:"<<name;
            continue;
        }
        setProperty(name, json_object.value(name));
    }

    return true;
}

QJsonObject JsonInfoBase::ToJson()
{
    QJsonObject json_object;
    foreach(const QByteArray &name, PropertyNames()){
        if(!property(name).isValid() || property(name).isNull()){
            continue;
        }
        json_object.insert(name, QJsonValue::fromVariant(property(name)));
    }
    return json_object;
}

void JsonInfoBase::ObservePropertyChanged(const QStringList &ignore_propertys)
{
    auto interested_propertys = PropertyNames();

    auto meta_object = this->metaObject();
    for (int i=0; i < meta_object->propertyCount(); i++){
        QMetaProperty meta_property = meta_object->property(i);
        if(!interested_propertys.contains(meta_property.name())){
            continue;
        }

        if(ignore_propertys.contains(meta_property.name())){
            continue;
        }

        if(meta_property.hasNotifySignal()){
            const QMetaMethod notify_signal = meta_property.notifySignal();
            const QMetaMethod property_changed = meta_object->method(meta_object->indexOfMethod("propertyChanged()"));
            connect(this, notify_signal, this, property_changed, Qt::UniqueConnection);
        }
    }
}

QList<QByteArray> JsonInfoBase::PropertyNames() const
{
    QList<QByteArray> property_names;
    auto meta_object = this->metaObject();
    for (int i=0; i < meta_object->propertyCount(); i++){
        property_names << meta_object->property(i).name();
    }
    property_names.removeAll("objectName");// 移除自带的
    return  property_names;
}

你可能感兴趣的:(qt,开发语言)