有两种可能的方法:
- 将所有设置加载到某个结构体中
- 按需加载值
哪种方法更好?
这取决于您将如何使用设置文件。您是否希望允许应用程序的用户动态更改文件中的设置(例如.ini文件)?还是必须通过GUI设置设置?
如果您正在使用某些GUI来更改设置,则建议您从静态类(例如)在应用程序启动时加载主要设置。
void SettingsManager::loadSettings()
{
// .ini format example
QSettings settings(FileName, QSettings::IniFormat);
IntegerSetting = settings.value("SettingName", default).toInt();
BooleanSetting = settings.value("SettingName", default).toBool();
// ...
}
那么,由于QSettings的优化,按需保存更改后的值不会有问题。
/**
* key is your setting name
* variant is your value (could be string, integer, boolean, etc.)
*/
void SettingsManager::writeSetting(const QString &key, const QVariant &variant)
{
QSettings settings(FileName, QSettings::IniFormat);
settings.setValue(key, variant);
}
如果您担心的话,可以将每个逻辑组的设置放在接口后面。然后,构建一个具体的类,使用QSettings按需检索设置。
如果您发现这是性能瓶颈,请构建一个具体的类来缓存设置。(我从未需要这样做。QSettings一直足够快。)
QSettings()
和调用QSettings::value()
几乎从来不会有问题,所有我的测量表明它非常非常快,几乎接近于0毫秒。关于你的问题:我会直接读取数据,即没有中间结构。再加上另一层只会增加复杂性,这不值得花费可能需要同步的努力。现在转到真正的问题。QSettings().setValue(key, value);
。因为这将立即在对象被销毁时进行保存操作并导致延迟。Settings
,该类具有静态方法并提供与QSettings
对象相似的API。在这个单例对象中,我只在实例化QApplication
后创建QSettings
对象,并在应用程序结束时销毁它。在我的代码中,每当需要时,我调用Settings::value(key)
或Settings::setValue(key, value)
。优点是只有在事件循环准备好时才保存设置。当然,这仍然需要50毫秒,但是可以确定它只会被调用一次,并将保存所有已经缓存的更改。与QSettings().setValue(key, value)
相比,这是一个很大的改进,后者每次都会调用保存,如果您进行多个此类调用,可能会阻塞您的UI。#pragma once
#include <QSettings>
/// Singleton! Create only one instance!
class Settings
{
public:
Settings();
~Settings();
static bool contains(const QString &key);
static QVariant value(const QString &key, const QVariant &defaultValue = QVariant());
static void setValue(const QString &key, const QVariant &value);
private:
static Settings *s_instance;
QSettings m_settings;
};
settings.cpp:
#include "settings.h"
Settings *Settings::s_instance = nullptr;
Settings::Settings()
{
Q_ASSERT(s_instance == nullptr);
s_instance = this;
}
Settings::~Settings()
{
Q_ASSERT(s_instance != nullptr);
s_instance = nullptr;
}
bool Settings::contains(const QString &key)
{
return s_instance->m_settings.contains(key);
}
QVariant Settings::value(const QString &key, const QVariant &defaultValue)
{
return s_instance->m_settings.value(key, defaultValue);
}
void Settings::setValue(const QString &key, const QVariant &value)
{
s_instance->m_settings.setValue(key, value);
}
main.cpp:
...
Application application; // must be created before settings
Settings settings; // create settings singleton
application.exec() // runs event loop - settings is stored whenever event loop is ready
// settings destroyed here
// application destroyed here
...
在代码的其他部分中,只需调用 Settings::setValue(key, value);
。
请注意,即使这种解决方案对于一些非常时间关键的用例来说还不够好。例如,通过拖动鼠标调整分隔器或窗口大小。您希望它流畅,并同时保存设置,对吧?为了实现平滑效果,您必须在拖动期间不保存它,而是在拖动完成后才保存。因此,不要在鼠标移动事件中保存设置。您只想在拖动完成后更改设置。要实现这一点,您将需要使用事件过滤器进行一些巧妙的技巧,也许继承和自定义Qt库存小部件或其他一些方式,根据您的需求而定。但这是不同的故事和不同的问题。
QSettings
的文档中,它表示已经进行了很好的优化。QSettings
时,我将其设置为与其示例类似,使用readSettings()
和writeSettings()
函数。 请参见此示例,在页面的中间位置。readSettings()
,QSettings对象被创建并按需加载值,并将所有设置保存在某个结构中。QSettings::setFormat
,然后在需要访问QSettings时,我创建具有默认参数的QSettings实例并访问设置。QSettings s;
int val = s.value("Some_Group/some_setting", default_value).toInt();
// ...
s.setValue("Some_Group/some_setting", val);