Qt6开发跨平台音乐播放器(四):通用配置类

1. 目标

客户端程序经常需要读取/保存配置,而配置项的内容各种各样,且可能存在多个配置文件,所以我们写一个通用的配置类,具备以下功能特性:

  • json格式

  • 读取/保存配置文件

  • 统一访问接口

  • 线程安全

2. 实现

创建一个configuration模块,包含一个Configuration类:

configuration
    - configuration.pri
    - configuration.h
    - configuration.cpp

2.1 json格式

在Qt程序中,一般使用QVariantMap/QVariantList来匹配json文档结构,而QFile读取文件返回的数据为QByteArray类型,Qt提供了QJsonDocument等类来实现QVariant和QByteArray之间的转换,我们封装一下:

// configuration.h
class Configuration {
...
private:
    QVariant json2variant(const QByteArray &json);
    QByteArray variant2json(const QVariant &variant);
}

// configuration.cpp
QVariant Configuration::json2variant(const QByteArray &json) {
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(json, &parseError);
    if (QJsonParseError::NoError != parseError.error) {
        return QVariant();
    }
    return doc.toVariant();
}

QByteArray Configuration::variant2json(const QVariant &variant) {
    QJsonDocument doc = QJsonDocument::fromVariant(variant);
    return doc.toJson();
}

2.2 读取/保存配置文件

定义2个protected类型的成员变量:

protected:
    QString m_path = "";         // 存储配置文件路径
    QVariantMap m_config;        // 存储配置

因为配置文件可能默认不存在,所以我们在读取接口中增加一个配置,如果配置文件不存在,可以选择报错或者返回空:

bool readFile(const QString &path, bool allowNotExist = false);
bool saveFile();

实现如下:

bool Configuration::readFile(const QString &path, bool allowNotExist) {
    if (!QFile::exists(path)) {
        if (!allowNotExist) {
            qCritical() << "configuration file not exists: " << path;
            return false;
        }

        m_path = path;
        m_config = QVariantMap();
        return true;
    }

    QFile file(path);
    if (!file.open(QIODevice::ReadOnly)) {
        qCritical() << "open file failed: " << file.errorString();
        return false;
    }

    QByteArray data = file.readAll();
    file.close();
    m_path = path;
    m_config = json2variant(data).toMap();
    return true;
}

bool Configuration::saveFile() {
    if (m_path.isEmpty()) {
        qCritical() << "configuration file's path cannot be empty";
        return false;
    }

    QFile file(m_path);
    if (!file.open(QIODevice::WriteOnly)) {
        qCritical() << "open configuration file failed: " << file.errorString();
        return false;
    }

    file.write(variant2json(m_config));
    file.flush();
    file.close();
    return true;
}

2.3 统一访问接口

得益于QVariant类型,我们可以方便地实现统一的读写接口,而不需要根据配置项类型来写不同的读写接口:

bool Configuration::setValue(const QString &key, const QVariant &variant) {
    m_config.insert(key, variant);
    return saveFile();
}

QVariant Configuration::value(const QString &key) {
    return m_config[key];
}

另外程序中有时需要一些临时配置,不需要写入配置文件,可以简单地新增一个成员变量,并增加读写接口:

// configuration.h
class Configuration {
public:
    ...
    QVariant tempValue(const QString &key);
    void setTempValue(const QString &key, const QVariant &variant);

protected:
    QVariantMap m_tempConfig;
}

// configuration.cpp
QVariant Configuration::tempValue(const QString &key) const { return m_tempConfig[key]; }

void Configuration::setTempValue(const QString &key, const QVariant &variant) {
    m_tempConfig.insert(key, variant);
}

如果是自定义类型,可以参考Creating Custom Qt Types | Qt Core 6.7.2

2.4 线程安全

配置类是典型的多读少写模式,用读写锁非常适合(读写锁具备读共享,写独占的特性),我们以QReadWriteLock为例子,在value和setValue两个函数中加入读写锁:

// configuration.h
class Configuration {
    ...
private:
    QReadWriteLock m_lock;
}

// configuration.cpp
QVariant Configuration::value(const QString &key) {
    QReadLocker locker(&m_lock);
    return m_config[key];
}

bool Configuration::setValue(const QString &key, const QVariant &variant) {
    QWriteLocker locker(&m_lock);
    m_config.insert(key, variant);
    return saveFile();
}

PS: 代码已经开源在github:linqiaqun/music-player: A cross platform music player (github.com) 欢迎star/fork/issue

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值