有两种可能的方式:
哪种方法更好?
答案 0 :(得分:8)
这取决于您使用设置文件的方式。您是否希望允许应用程序的用户动态更改文件中的设置(例如.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);
}
答案 1 :(得分:3)
如果您担心,可以将每个逻辑设置组放在界面后面。然后,构建一个使用QSettings按需检索设置的具体类。
如果您发现这是性能瓶颈,请构建一个缓存设置的具体类。 (我从来不需要这样做.QSettings一直很快。)
答案 2 :(得分:2)
在QSettings
的文档中,它说它已经过优化。
在内部,它将QStrings的地图保存到QVariants。所有访问器方法都非常有用,并且易于使用。
当我使用QSettings
时,我将其设置与具有readSettings()
和writeSettings()
功能的示例类似。请在页面的一半左右查看此example。
当我调用readSettings()
创建QSettings对象时,它会根据需要加载值,并将所有设置保留在某个结构中。
所以在我的main函数中,我确保设置我的应用程序名称和组织名称,并且我也使用QSettings::setFormat
,然后在我想访问QSettings之后,我创建了一个QSettings实例默认参数并访问设置。
QSettings s;
int val = s.value("Some_Group/some_setting", default_value).toInt();
// ...
s.setValue("Some_Group/some_setting", val);
答案 3 :(得分:0)
我不会完全回答您的问题,因为您提出的问题是错误的;)您正在询问阅读设置。通过构造QSettings()
进行读取并调用QSettings::value()
几乎从来不是问题,我的所有测量都表明它非常快,非常接近0毫秒。关于您的问题:我将直接读取数据,即没有中介结构。拥有另一层只是复杂而已,不值得为可能的同步付出努力。现在是真正的问题。
但是,最主要的问题是写入设置。如果您使用本地存储进行设置,这在Windows上也相当快,这是Windows注册表(Windows的默认设置)。注册表是由操作系统优化的,缓存在RAM中,因此写入is也不会引起延迟。但是在Linux上,这似乎是一个非常不同的故事。接下来的内容与Linux有关(在我的情况下为Ubuntu和Kubuntu)。
我没有详细研究Qt的源代码,但是我的所有测量都表明,在进行任何更改后将设置写入磁盘至少需要在普通磁盘上花费约50毫秒,SSD可能会更快。在我看来,当QSettings
对象数据已更改且对象被销毁时,或当应用程序事件循环准备好执行某些工作(即不忙于重绘或处理其他事件)时,将调用保存操作。然后将设置刷新到磁盘。
因此,无论您关注的速度如何,我都警告不要调用此QSettings().setValue(key, value);
。因为这将导致对象破坏后立即进行保存操作,并导致延迟。
如果仅对设置调用一次保存操作,则节省时间不是问题,例如,关闭应用程序时,您可以轻松地支付50毫秒。但这通常不是您想要的。您希望动态保存应用程序状态。换句话说,当您在应用程序中进行某些更改,然后在不关闭第一个实例的情况下,打开了该应用程序的另一个实例,并且您希望新实例已经具有新的设置。在这种情况下,您必须在进行任何更改后立即保存所有内容,而不仅仅是保存应用程序关闭时。然后,节省时间成为一个大问题。
我怎么做。我创建一个具有静态方法的单例类Settings
,并提供与QSettings
对象类似的API。在此单例对象中,仅创建一次QSettings
对象(实例化QApplication
之后),并在应用程序结束时仅销毁一次。在我的代码中,我随时可以调用Settings::value(key)
或Settings::setValue(key, value)
。优点是仅在事件循环准备就绪时才保存设置。当然,这仍然需要50毫秒,但是可以确定的是,它只会被调用一次,并且会保存所有已缓存的更改。与QSettings().setValue(key, value)
相比,这是一个很大的改进,#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;
};
每次都会调用save,并且如果您多次进行这样的调用会阻塞UI。
您当然可以通过多种方式实现单例。我使用的是这个:
settings.h:
#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);
}
settings.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
...
main.cpp:
Settings::setValue(key, value);
在其余代码中,只需调用getText()
。
请注意,即使对于某些时间紧迫的用例,此解决方案也不够好。例如,考虑通过拖动鼠标来调整拆分器或窗口的大小。您希望它平滑并同时保存设置,对吗?为了获得平滑度,您一定不要在拖动过程中保存它,而只能在拖动完成之后保存它。因此,请勿在鼠标移动事件中保存设置。您只想在拖动完成后更改设置。为了实现这一点,您将不得不使用事件过滤器做一些巧妙的技巧,也许继承继承并自定义库存的Qt小部件,或者根据您的需要定制其他东西。但这是不同的故事和不同的问题。