Как загрузить настройки в приложение Qt с помощью QSettings

Есть два возможных пути:

  • загрузить все настройки в некоторую структуру
  • загружать значения по запросу

Какой подход лучше?


person user1180567    schedule 16.01.2013    source источник
comment
Это текстовый редактор. Настройки, такие как максимизированные окна, последние файлы и так далее.   -  person user1180567    schedule 16.01.2013


Ответы (4)


Это зависит от того, как вы будете использовать свой файл настроек. Вы хотите разрешить пользователю вашего приложения динамически изменять настройки в файле (например, в файле .ini)? Или настройки должны быть установлены графическим интерфейсом?

Если вы используете какой-либо графический интерфейс для изменения настроек, я советую вам загрузить основные настройки в начале вашего приложения, например, из статического класса.

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);
}
person Kévin Renella    schedule 16.01.2013

Если вас это беспокоит, вы можете разместить каждую логическую группу настроек за интерфейсом. Затем создайте конкретный класс, который использует QSettings для получения настроек по требованию.

Если вы обнаружите, что это является узким местом в производительности, создайте конкретный класс, который кэширует настройки. (Мне никогда не приходилось этого делать. QSettings всегда работал достаточно быстро.)

person cgmb    schedule 16.01.2013

Я не буду отвечать на ваш вопрос именно потому, что вы задаете неправильный вопрос ;) Вы спрашиваете о настройках чтения. Чтение с построением QSettings() и вызовом QSettings::value() почти никогда не вызывает проблем, все мои измерения показывают, что это очень-очень быстро, довольно близко к 0 мс. Что касается вашего вопроса: я бы читал данные напрямую, то есть без промежуточной структуры. Наличие еще одного слоя — это просто усложнение, оно не стоит усилий с возможными синхронизациями. А теперь собственно вопрос.

Однако большая проблема заключается в записи в настройки. Это также довольно быстро в Windows, если вы используете собственное хранилище для настроек, то есть реестр Windows (по умолчанию в Windows). Реестр оптимизируется ОС, кэшируется в оперативной памяти, поэтому запись в него также не вызывает задержек. Однако в Linux это, кажется, совсем другая история. Дальнейшее относится к Linux (в моем случае Ubuntu и Kubuntu).

Я не изучал подробно исходный код Qt, но все мои измерения показывают, что запись настроек на диск после любого изменения занимает не менее 50 мс на обычном диске, SSD может быть несколько быстрее. Мне кажется, что операция сохранения вызывается, когда QSettings данные объекта были изменены и объект уничтожен, или когда цикл событий приложения готов выполнить какую-то работу (т.е. не занят перерисовкой или обработкой других событий). Затем настройки сбрасываются на диск.

Поэтому я бы предостерег вас от использования QSettings().setValue(key, value); везде, где вас интересует скорость. Потому что это вызовет немедленную операцию сохранения при уничтожении объекта и вызовет задержку.

Экономия времени не является проблемой, если вы вызываете операцию сохранения настроек только один раз, например, при закрытии приложения вы можете легко заплатить 50 мс. Но обычно это не то, что вы хотите. Вы хотите, чтобы состояние вашего приложения сохранялось динамически. Другими словами, когда вы что-то меняете в своем приложении, а затем, не закрывая первый экземпляр, вы открываете другой экземпляр этого приложения, вы ожидаете, что новый экземпляр уже будет иметь новые настройки. В этом случае вы должны сохранять все, как только вносятся какие-либо изменения, а не только при закрытии приложения. И тогда экономия времени становится большой проблемой.

Как я это делаю. Я создаю одноэлементный класс Settings, который имеет статические методы и предоставляет API, аналогичный объекту QSettings. В этом одноэлементном объекте я создаю объект QSettings только один раз (сразу после создания экземпляра QApplication) и уничтожаю его только один раз, когда приложение завершает работу. В моем коде я вызываю Settings::value(key) или Settings::setValue(key, value) всякий раз, когда мне нужно. Преимущество в том, что настройки сохраняются только тогда, когда событийный цикл к этому готов. Конечно, это по-прежнему займет 50 мс, но точно будет вызвано только один раз и сохранит все изменения, которые были кешированы за это время. И это большое улучшение по сравнению с QSettings().setValue(key, value), который будет вызывать save каждый раз и может блокировать ваш пользовательский интерфейс, если вы делаете несколько таких вызовов.

Вы, безусловно, можете реализовать синглтон разными способами. Тот, который я использую, это:

настройки.ч:

#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;
};

настройки.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);
}

основной.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 или, может быть, что-то еще, просто в соответствии с вашими потребностями. Но это другая история и другой вопрос.

person V.K.    schedule 09.11.2019

В документации QSettings говорится, что он действительно хорошо оптимизирован. .

Внутри он хранит карту QStrings в 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);
person phyatt    schedule 16.01.2013