通用屬性對象PropertyObject,支持所有屬性導出

一、通用屬性對象類

這是以前造的一個廢輪子,不過需求改變,不用了,代碼寫好了,也不能浪費,發出來。思路可以複用。

如何將一個對象的所有屬性導出爲QString,並支持不同對象的層次組合。

定義一個通用的屬性對象類,如下:

PropertyObject.h

#ifndef PROPERTYOBJECT_H
#define PROPERTYOBJECT_H

#include <QObject>
#include <QVariant>
#include <QDataStream>

/**
 * @brief The PropertyObject class
 * 通用屬性對象
 */
class PropertyObject : public QObject
{
    Q_OBJECT
public:
    explicit PropertyObject(QObject *parent = nullptr);
    PropertyObject(const PropertyObject& obj);

    PropertyObject& operator=(const PropertyObject &obj);
    operator QVariant() const { return QVariant::fromValue(*this); }

    static void copyProperties(QObject *dst, const QObject *src);

private:
    class RegisterType
    {
    public:
        RegisterType();
    };
    static RegisterType autoRegType; ///< 實現自動註冊PropertyObject類型
};
Q_DECLARE_METATYPE(PropertyObject)

QDataStream& operator<<(QDataStream &out, const PropertyObject& info);
QDataStream& operator>>(QDataStream &in, PropertyObject& info);

#endif // PROPERTYOBJECT_H

PropertyObject.cpp

#include "PropertyObject.h"

#define EQUAL_SYMBOL           "="
#define SEPARATOR_SYMBOL       ";"
#define END_SYMBOL             "$"

PropertyObject::RegisterType PropertyObject::autoRegType;

/**
 * @brief PropertyObject::PropertyObject
 * @param parent 父節點
 */
PropertyObject::PropertyObject(QObject *parent)
    : QObject(parent)
{

}

/**
 * @brief PropertyObject::PropertyObject
 * 拷貝構造函數:
 * 因爲在保存此對象時,需要放入QVariant進行裝箱,期間會有對象拷貝操作,
 * 然而QObject並沒有拷貝構造函數,故我們繼承於QObject,然後重寫拷貝構造函數,
 * 完成obj對象中property的拷貝。
 * @param obj 被拷貝對象
 */
PropertyObject::PropertyObject(const PropertyObject &obj)
{
    copyProperties(this, &obj);
}

/**
 * @brief PropertyObject::operator =
 * 重載等號操作符
 * @param obj 等號右值
 * @return 等號左值引用,即當前對象引用
 */
PropertyObject &PropertyObject::operator=(const PropertyObject &obj)
{
    copyProperties(this, &obj);
    return *this;
}

/**
 * @brief PropertyObject::copyProperties
 * 拷貝屬性列表
 * @param dst 目的對象
 * @param src 源對象
 */
void PropertyObject::copyProperties(QObject *dst, const QObject *src)
{
    QList<QByteArray> names = src->dynamicPropertyNames();
    for (int i = 0; i < names.size(); i++)
    {
        const char* name = names.at(i).data();
        QVariant value = src->property(name);
        dst->setProperty(name, value);
    }
}

/**
 * @brief operator <<
 * 重載插入操作符,將屬性對象序列化
 * @param out 輸出流
 * @param info 屬性對象
 * @return 輸出流引用
 */
QDataStream& operator<<(QDataStream &out, const PropertyObject& info)
{
    QList<QByteArray> properties = info.dynamicPropertyNames();
    for (int i = 0; i < properties.size(); i++)
    {
        const char* name = properties.at(i).data();
        QVariant value = info.property(name);

        out << QString(name) << QString(EQUAL_SYMBOL) << value; // 注意寫入類型必須與讀取類型一致
        if (i != properties.size() - 1)
        {
            out << QString(SEPARATOR_SYMBOL);
        }
        else
        {
            out << QString(END_SYMBOL);
        }
    }
    return out;
}

/**
 * @brief operator >>
 * 重載讀取操作符,將輸入流反序列化爲屬性對象
 * @param in 輸入流
 * @param info 屬性對象
 * @return 輸入流引用
 */
QDataStream& operator>>(QDataStream &in, PropertyObject& info)
{
    QString separator;
    do //注意寫入類型必須與讀取類型一致
    {
        QString name;
        in >> name;

        QString equal;
        in >> equal;
        if (equal != QString(EQUAL_SYMBOL))
            break;

        QVariant value;
        in >> value;

        info.setProperty(name.toStdString().c_str(), value);

        separator.clear();
        in >> separator;
    } while (separator == QString(SEPARATOR_SYMBOL));
    return in;
}

PropertyObject::RegisterType::RegisterType()
{
    qRegisterMetaType<PropertyObject>("PropertyObject");
    qRegisterMetaTypeStreamOperators<PropertyObject>("PropertyObject");
}

其中涉及到的知識,主要是《利用Qt的QSetting類存儲自定義數據類型所需準備》。

二、測試代碼

ApplicationSetting類實現如下:

ApplicationSetting::ApplicationSetting()
{
    setting = new QSettings(qApp->applicationDirPath() + "/setting.ini", QSettings::IniFormat);
}

ApplicationSetting::~ApplicationSetting()
{
    delete setting;
    setting = nullptr;
}

void ApplicationSetting::setValue(const QString &key, const QVariant &value)
{
    setting->setValue(key, value);
}

QVariant ApplicationSetting::value(const QString &key, const QVariant &defaultValue) const
{
    return setting->value(key, defaultValue);
}

1. 測試寫入、讀取結構體對象

// 測試寫入、讀取結構體對象
void MainWindow::testStructObject(ApplicationSetting *setting)
{
    // 寫入
    PropertyObject src;
    src.setProperty("string", "456");
    src.setProperty("int", 80);
    src.setProperty("float", 58.9);
    src.setProperty("bool", true);
    src.setProperty("QSize", QSize(20, 50));
    setting->setValue("AppAttribute/Struct", src);

    // 讀取
    QVariant var = setting->value("AppAttribute/Struct");
    PropertyObject dst = var.value<PropertyObject>();
    print(&dst);
}

運行結果:
在這裏插入圖片描述

2. 測試寫入、讀取結構體下面嵌套結構體

// 測試寫入、讀取結構體下面嵌套結構體
void MainWindow::testNestedStructObject(ApplicationSetting *setting)
{
    // 寫入
    PropertyObject src;
    src.setProperty("string", "456");
    src.setProperty("int", 80);

    PropertyObject nested;
    nested.setProperty("int", 59);
    nested.setProperty("float", 25.3);
    src.setProperty("nested", nested);

    setting->setValue("AppAttribute/NestedStruct", src);

    // 讀取
    QVariant var = setting->value("AppAttribute/NestedStruct");
    PropertyObject dst = var.value<PropertyObject>();
    print(&dst);
}

運行結果:
在這裏插入圖片描述

3. 測試寫入、讀取結構體對象列表

// 測試寫入、讀取結構體對象列表
void MainWindow::testStructObjectList(ApplicationSetting *setting)
{
    // 寫入
    PropertyObject list;

    PropertyObject member0;
    member0.setProperty("int", 23);
    member0.setProperty("float", 29.3);
    list.setProperty("member0", member0);

    PropertyObject member1;
    member1.setProperty("int", 89);
    member1.setProperty("bool", true);
    list.setProperty("member1", member1);

    PropertyObject member2;
    member2.setProperty("string", "test");
    member2.setProperty("float", 2.3);
    list.setProperty("member2", member2);

    setting->setValue("AppAttribute/List", list);

    // 讀取
    QVariant var = setting->value("AppAttribute/List");
    PropertyObject dst = var.value<PropertyObject>();
    print(&dst);
}

運行結果:
在這裏插入圖片描述

4. 測試寫入、讀取結構體對象樹

// 測試寫入、讀取結構體對象樹
void MainWindow::testStructObjectTree(ApplicationSetting *setting)
{
    // 寫入
    PropertyObject root;

    // item0
    PropertyObject item0;

    PropertyObject item0_0;
    item0_0.setProperty("int", 96);

    PropertyObject item0_1;
    item0_1.setProperty("int", 36);

    item0.setProperty("item0_0", item0_0);// item0添加節點0
    item0.setProperty("item0_1", item0_1);// item0添加節點1

    // item1
    PropertyObject item1;

    PropertyObject item1_0;
    item1_0.setProperty("int", 15);

    PropertyObject item1_1;
    item1_1.setProperty("int", 25);

    item1.setProperty("item1_0", item1_0);// item1添加節點0
    item1.setProperty("item1_1", item1_1);// item1添加節點1

    root.setProperty("item0", item0);// root添加節點item0
    root.setProperty("item1", item1);// root添加節點item1

    setting->setValue("AppAttribute/Tree", root);

    // 讀取
    QVariant var = setting->value("AppAttribute/Tree");
    PropertyObject dst = var.value<PropertyObject>();
    print(&dst);
}

運行結果:
在這裏插入圖片描述

代碼地址:

https://gitee.com/bailiyang/cdemo/tree/master/Qt/38QSetting/PropertyObjectSetting


===================================================

===================================================

業餘時間不定期更新一些想法、思考文章,歡迎關注,共同探討,沉澱技術!

            

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章