如何使用QSettings在Qt应用程序中加载设置

10

有两种可能的方法:

  • 将所有设置加载到某个结构体中
  • 按需加载值

哪种方法更好?


这是一个文本编辑器。可以设置窗口最大化,最近打开的文件等等。 - user1180567
4个回答

14

这取决于您将如何使用设置文件。您是否希望允许应用程序的用户动态更改文件中的设置(例如.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);
}

4

如果您担心的话,可以将每个逻辑组的设置放在接口后面。然后,构建一个具体的类,使用QSettings按需检索设置。

如果您发现这是性能瓶颈,请构建一个具体的类来缓存设置。(我从未需要这样做。QSettings一直足够快。)


4
我不会直接回答你的问题,因为你问了一个错误的问题;) 你在询问关于读取设置的内容。使用构造函数QSettings()和调用QSettings::value()几乎从来不会有问题,所有我的测量表明它非常非常快,几乎接近于0毫秒。关于你的问题:我会直接读取数据,即没有中间结构。再加上另一层只会增加复杂性,这不值得花费可能需要同步的努力。现在转到真正的问题。
然而,写入设置是一个很大的问题。如果您在Windows上使用本地存储设置,并且默认情况下使用Windows注册表,则在Windows上也很快。注册表由操作系统进行优化,在RAM中缓存,因此对其进行写入也不会导致延迟。但是,在Linux上,这似乎是一种非常不同的情况。下面是与Linux(我个人使用的是Ubuntu和Kubuntu)相关的情况。
我没有详细探索Qt的源代码,但是我的所有测量结果显示,每次更改后将设置写入磁盘至少需要50毫秒,SSD可能会稍微快一些。在我的看来,当QSettings对象数据已更改并销毁该对象,或者应用程序事件循环准备好执行某些工作(即不忙于重绘或处理其他事件)时,就会调用保存操作。
因此,我建议您在关注速度的情况下避免随处调用QSettings().setValue(key, value);。因为这将立即在对象被销毁时进行保存操作并导致延迟。
如果只有一次调用设置的保存操作,例如在关闭应用程序时,保存时间不是问题,则可以轻松支付50毫秒。但这通常不是你想要的。你希望你的应用程序状态能够动态保存。换句话说,当你更改应用程序的某些内容并且然后,在没有关闭第一个实例的情况下打开该应用程序的另一个实例,并且你希望新实例已经具有新的设置。在这种情况下,您必须尽快保存所有更改,而不仅仅是在应用程序关闭时才保存。然后,保存时间就成为一个大问题。
我的做法是创建单例类Settings,该类具有静态方法并提供与QSettings对象相似的API。在这个单例对象中,我只在实例化QApplication后创建QSettings对象,并在应用程序结束时销毁它。在我的代码中,每当需要时,我调用Settings::value(key)Settings::setValue(key, value)。优点是只有在事件循环准备好时才保存设置。当然,这仍然需要50毫秒,但是可以确定它只会被调用一次,并将保存所有已经缓存的更改。与QSettings().setValue(key, value)相比,这是一个很大的改进,后者每次都会调用保存,如果您进行多个此类调用,可能会阻塞您的UI。
你可以用许多方式实现单例。我使用的一种方式是:
settings.h:
#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库存小部件或其他一些方式,根据您的需求而定。但这是不同的故事和不同的问题。


2
QSettings的文档中,它表示已经进行了很好的优化。
在内部,它保留了一个QString到QVariant的映射。 所有访问器方法都非常有用且易于使用。
当我使用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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接