1 前言
描述为什么需要配置类,以及为什么使用单利
工作过程中总是需要修改用于内网本地测试的版本或者外网测试版本,此时如果将参数直接导出到配置文件config.ini
,只修改单个参数即可。当然很多地方也是如此,如修改用户界面主题等,使用配置文件保存配置参数是比较好的做法。在Qt中提供了QSettings类来格式导出和导入配置文件。
另外,我希望能够全局访问配置文件,并且要考虑多线程访问的情况,因此考虑实现为单例,在单例中需要考虑如何避免出现竞态条件?如何析构此单例类?
因此,本篇文章的主要目的是:
- 创建单例类,可应对多线程情况,且能够正确析构
- 结合QSettings,生成配置文件和读取配置文件
2 QSettings类
此处只描述本次使用的情景,还有额外的其他使用方法,如设置写入的位置是用户范围还是系统范围,设置公司、机构、软件名等等。
ini文件
.ini 文件是Initialization File的缩写,即初始化文件。INI文件被用来对操作系统或特定程序初始化或进行参数设置,以实现不同用户的要求。一般不用直接编辑这些.ini文件,应用程序的图形界面即可操作以实现相同的功能。
Qt中可使用QSettings存储.ini文件,.ini文件有[section]、key=value组成。其中section表示段,用于区分项目中不同的部分配置参数,key和value为键值对,value为QVariant类型。
[transmitData]
port=9600
station=1
[canvas]
gridStyle=true
width=500
height=400
写ini文件
将配置参数写入QSettings对象如下:
//创建此对象,也可创建在堆上
QSettings settings("/home/petra/misc/myapp.ini", QSettings::IniFormat);//此处直接填写的是文件路径
setting.setIniCodec(QTextCodec::codecForName("UTF-8"));//设置文件编码,配置文件中使用中文时,这是必须的,否则乱码
//beginGroup和endGroup可设置一级标题,中间的setValue为设置的参数
settings.beginGroup("mainwindow");
settings.setValue("size", win->size());
settings.setValue("fullScreen", win->isFullScreen());
settings.endGroup();
-
QSettings::Format
取值为
QSettings::NativeFormat
在windows平台可以读写windows注册表;
QSettings::IniFormat
可以读写ini格式的配置文件 -
小心使用分号和逗号
分号在ini配置文件中是注释符号
逗号在ini配置文件中被解析为数组分隔符, -
层级结构除了使用上述的写法,也可以使用前缀的方式:
settings.setValue("mainwindow/size", win->size());
-
编码
配置文件如果有中文就需要设置为setting.setIniCodec(QTextCodec::codecForName("UTF-8"))
保存float有问题,将其转换为double就好 -
其他
创建QSettings后,如果指定文件不存在,则创建文件
setValue不会立刻写入本地磁盘,只是修改了缓冲参数,因此在修改参数后,可调用settings->sync()或者syncFlushFileBuffers来同步
QSettings* setting = new QSettings("config.ini", QSettings::IniFormat);
setting->setIniCodec(QTextCodec::codecForName("UTF-8"));
setting->beginGroup("Transmit");
setting->setValue("port", 9600);
setting->setValue("width", 500);
setting->setValue("height", 400);
setting->endGroup();
setting->sync();
读取Ini文件
QSettings settings("/home/petra/misc/myapp.ini", QSettings::IniFormat);//此处直接填写的是文件路径
//beginGroup和endGroup可设置一级标题,中间的setValue为设置的参数
settings.beginGroup("mainwindow");
QSize size = settings.value("size").toSize();
QColor = settings.value("color").value<QColor>();
settings.endGroup();
-
也可使用前缀读取方式
QSize size = settings.value("mainwindow\size").toSize();
-
默认值
如果不存在该标签项,则可以在第二个参数处填写默认值,否则就直接返回空QVariant() -
对于QGUI的部分类型,如QPixmap、QColor等无法直接toColor,可使用
QVariant.value<QColor>
来实现 -
自定义类型可以使用
qRegisterMetaType(MyStruct)
注册
3 单例模式
为了在全局使用同一个配置文件,或者日志文件,使用单例模式是比较适合的。使用步骤:
- 私有化构造函数、拷贝构造函数、析构函数、operator=等
- 内部创建静态对象指针,并创建静态函数返回该对象指针
- 注意
如何避免多线程形成竞态条件?
如何更好的析构该单例对象?
class ConfigParam
{
public:
static ConfigParam* GetInstance()
{
if(m_pInstance == nullptr)//采用双判断,是为了减少不断的加锁、解锁的操作
{
static QMutex mutex;
mutex.lock();
if(m_pInstance == nullptr)
m_pInstance = new ConfigParam;//加锁是为了避免线程检查条件后,开始创建对象还未创建时,切换线程,其他线程也继续创建对象
mutex.unlock();
}
return m_pInstance;
}
class Deletor
{
public:
~Deletor()
{
delete m_pInstance;
m_pInstance = nullptr;
}
};
private:
static ConfigParam* m_pInstance;
static Deletor deleter;//创建为静态对象,在清理全局区时,调用该软件的析构函数,会释放单例对象
private:
ConfigParam(){}
~ConfigParam(){}
ConfigParam(const ConfigParam&){}
ConfigParam& operator=(const ConfigParam&){return *this;}
};
ConfigParam* ConfigParam::m_pInstance = nullptr;
ConfigParam::Deletor ConfigParam::deleter;
5 结语
如果全神贯注的写博客,还是能及时完成一篇的,继续加油,不断积累啊。
希望有一天坐着高铁去台湾省,看看小学课文的日月潭,虽然早已干枯~
6 参考文献
【1】总结:Qt读写ini配置文件(QSettings)
【2】QT学习之如何读写配置文件(QSettings)
【3】QSettings
【4】单例模式——讲解比较通透