Почему я не могу сохранить QVariantMap в QSettings?

Почему это было возможно в Qt 5.2 и ранее и хранит данные в следующем формате:

key=@Variant(\0\0\0\b\0\0\0)

но теперь проблема в Qt 5.11?! Следующий код

QVariantMap projectsMap;
for (auto project : projects)
    projectsMap.insert(key, value);

settings->setValue("Group/projects", projectsMap);

выполняется правильно, но ничего не сохраняет в файле ini.

qRegisterMetaTypeStreamOperators<QVariantMap>("QVariantMap");

тоже не помогает. Как это хранить, в чем тут проблема?


person Aleksey Kontsevich    schedule 12.10.2018    source источник
comment
Не могу воспроизвести (Linux + Qt5.11.2). projectsMapопределенно не пусто? Все ли типы, содержащиеся в экземплярах QVariant, зарегистрированы в системе метатипов Qt?   -  person G.M.    schedule 12.10.2018
comment
@GM, да, не пусто: проверка с помощью qDebug() Содержит QString для ключа и значения. Проблема в том, что даже ключ "projects" не создается в "Group" в ini-файле.   -  person Aleksey Kontsevich    schedule 12.10.2018
comment
@G.M., проблема была в settings->sync(): описанные выше операции сохранения настроек выполняются в деструкторе, и я думал, что деструктор настроек должен вызывать sync() автоматически - кажется, это не так, поэтому явный вызов sync() сейчас - работает нормально! Спасибо!   -  person Aleksey Kontsevich    schedule 12.10.2018
comment
Этот вопрос является недостаточным, пока не будет предоставлен полный тестовый пример. В каком «деструкторе» вы задаете настройки? В деструкторах нет ничего особенного, и QSettings это тоже не волнует.   -  person Kuba hasn't forgotten Monica    schedule 12.10.2018
comment
@KubaOber, в настройках моего класса объявлено как QPointer<QSettings> settings; и кажется, что при уничтожении объекта settings->sync() не вызывается. Назовите это явно.   -  person Aleksey Kontsevich    schedule 12.10.2018
comment
Комментарии не являются подходящим способом исправления неверных вопросов: вы должны отредактировать свой вопрос, чтобы исправить его. Включите полный тестовый пример: main.cpp, который начинается с #include <QtWidgets> и заканчивается необязательным #include "main.moc"   -  person Kuba hasn't forgotten Monica    schedule 12.10.2018


Ответы (1)


Не храните QSettings: он не предназначен для такого использования. Вы должны использовать новый экземпляр QSettings каждый раз, когда вы меняете настройки. Ваш деструктор должен выглядеть следующим образом:

MyClass::~MyClass() {
  QSettings s;
  s.setValue(kFoo, this->m_bar);
  …
}

QSettings — это эфемерный дескриптор системы настроек, его реализация дешева. Вы сливаете его, потому что QPointer ничего не уничтожает: это не указатель-владелец.

person Kuba hasn't forgotten Monica    schedule 12.10.2018
comment
Понятно, пропустил инфу про QPointer, теперь понятно, почему люди используют std::unique_ptr в приложениях Qt, так как QPointer не является аналогом. Изменится на std::unique_ptr, чтобы автоматически очистить объект. Ваш способ не работает для меня, так как объект настроек принадлежит классу менеджера и используется многими контролируемыми классами. Только нужно правильно очистить объект настроек - будем использовать для этого std::unique_ptr или просто QSettings settings;, так как unique_ptr не так удобен, как QPointer. - person Aleksey Kontsevich; 13.10.2018
comment
Я же говорю, что с объектом настроек так делать нельзя! Не «управлять»! Вы изобретаете для себя работу, которую не стоит даже пытаться делать. Создавайте экземпляр каждый раз, когда вы хотите его использовать. Он был разработан для использования таким образом. И вместо того, чтобы использовать указатель, вы должны хранить вещи по значению. Каково назначение указателя? Нет ни одного. Только использование указателя времени жизни объекта отличается от времени жизни «менеджера». В современном C++ большинство таких «менеджеров» должны быть простой структурой со значениями, некоторые из них, возможно, заключены в optional. - person Kuba hasn't forgotten Monica; 13.10.2018
comment
Управляемые объекты ничего не знают о настройках, не хранят их, доступ через manager()-›setting()-›, поэтому мне нужен указатель: чтобы разделить один экземпляр настроек между менеджером и управляемыми объектами. - person Aleksey Kontsevich; 14.10.2018
comment
Управляемые объекты должны создавать экземпляр QSettings всякий раз, когда им это нужно. Если setting() возвращает что-то отличное от указателя на QSettings, то есть ваш собственный класс, то этот класс должен создать объект настроек по мере необходимости — временно. На самом деле вы можете обернуть QSettings в свой собственный класс Settings, который могут использовать другие объекты; этот класс будет предоставлять настройки косвенно, например, через геттеры/сеттеры. - person Kuba hasn't forgotten Monica; 15.10.2018
comment
Этот ответ заставил меня потратить некоторое время, пытаясь не использовать QSettings *. Так что, если это не так, как это было предназначено для использования? QThread также не был разработан для работы с потоком в run(); но теперь это поддерживаемое использование. QSettings *pSettings; у меня отлично работает. Я ненавижу каждый раз настраивать все параметры. Если вы new передадите ему родительский аргумент, он автоматически позаботится о любом освобождении памяти, которое должно произойти. - person CodeLurker; 19.12.2019
comment
Кажется, я не совсем ясно выразился: нет смысла повторно использовать QSettings. Просто сделайте один, когда вам это нужно. Он не забудет никаких промежуточных настроек. Я не знаю, что вы имеете в виду, говоря, что я ненавижу каждый раз настраивать все параметры - все дело в том, что вам не нужно этого делать. У вас может быть такой код, как QSettings settings; if (settings.value(kSomeSettingKey).toBool()) { /* do something here */ }. Больше ничего не нужно. Что касается kFoo: это многократно используемая константа, принадлежащая где-то в файле .h, поэтому вы можете использовать ее везде, где вам нужно получить доступ к настройкам, чтобы у вас не было ошибок из-за опечаток. - person Kuba hasn't forgotten Monica; 19.12.2019