Is there a way of setting the actual default path of QSettings
storage (with or without filename), and preventing QSettings
from appending stuff like organization name to that path? That with the requirement that the further usages of QSettings
stay the same and have no knowledge of the settings storage path.
Consider the use case. In my main I have the following code:
QApplication a(argc, argv);
...
QCoreApplication::setApplicationName(APP_NAME);
QCoreApplication::setOrganizationDomain(MY_DOMAIN);
QCoreApplication::setOrganizationName(MY_ORGANIZATION);
QCoreApplication::setApplicationVersion(APP_VERSION);
...
a.exec();
and all over my project I use QSettings
like this:
QSettings settings;
settings.setValue(somePath, someValue);
someOtherValue = settings.value(someOtherPath, someDefault);
Now I got a requirement to make the application portable. So I change the code in main to this:
QApplication a(argc, argv);
...
QCoreApplication::setApplicationName(APP_NAME);
QCoreApplication::setOrganizationDomain(MY_DOMAIN);
QCoreApplication::setOrganizationName(MY_ORGANIZATION);
QCoreApplication::setApplicationVersion(APP_VERSION);
if (PORTABLE) {
QSettings::setDefaultFormat(QSettings::IniFormat);
QString settingsPath = a.applicationDirPath() + "/settings";
QSettings::setPath(QSettings::defaultFormat(), QSettings::UserScope, settingsPath);
QSettings::setPath(QSettings::defaultFormat(), QSettings::SystemScope, settingsPath);
}
...
a.exec();
But instead of the settings being written to APP_PATH/settings/
the actual location becomes APP_PATH/settings/ORGANIZATION_NAME/
, which in my opinion is a bit too ugly.
I would like to set up QSettings
to store the settings in a specific location. The user code must not be aware of the storage location change. Ideally set up QSettings
in main, and use the objects, constructed with default constructor further on.
What I have found out is that only the QSettings(const QString &fileName, QSettings::Format format, QObject *parent = nullptr)
constructor can be used to really set the storage location. All other constructors keep adding stuff to the path. And as I don't want the user code to be aware of the application portability, using different constructors each time is not an option.
I thought about subclassing QSettings
kind of like this to fix setPath()
:
class Settings : public QSettings {
private:
static QString mPath = "";
public:
Settings(QObject *parent = nullptr) :
QSettings(
mPath.isEmpty() ?
QSettings() :
QSettings(mPath, Settings::defaultFormat, parent)
)
{}
static void setPath(QSettings::Format format, QSettings::Scope scope, const QString &path)
{
mPath = path;
QSettings::setPath(format, scope, path);
}
}
but it's impossible because QSettings
doesn't have a copy constructor. Constructing the base with QSettings(mPath, Settings::defaultFormat, parent)
in every case, but setting mPath
to the default path in case it hasn't been set, can't be done too, because there is no method to get a default QSettings
storage path (there is setPath
, but no getPath
; there is getFileName
and no setFileName
; deriving a "path" from fileName is error prone because Qt adds some "magic", like that organization name, to the "path" to derive fileName, and that "magic" isn't really documented).
So the only solutions I came up with are:
QSettings
object on a heap with the appropriate
constructor, and returns a pointer to it. That has a downside of
having to manage the memory of that dynamically constructed object,
hence unreasonably added complexity. Also it adds another entity
that depends on the application portability.QSettings
object will exist over entire application lifetime.So I have a feeling that I'm definitely missing something here, and I wonder if there is a simpler solution to this problem.
Side note: Currently I solved this by setting my organization name to "settings" if the app is portable, so the application settings are stored in APP_FOLDER/settings/APP_NAME.ini. A dirty hack, but saves a lot of complexity. I don't really use the organization name anywhere anyway.
My understanding is that you essentially want the QSettings
to be constructed in one of two ways based on whether or not a specific path has been set.
Instead of inheriting from QSettings
you could create a class with an overloaded pointer to member ->
operator that returns a pointer to a QSettings
instance. That way the initialization of the QSettings
can be deferred until required and make use of whatever information has been set.
class settings {
public:
static void set_path (QSettings::Format format, QSettings::Scope scope, const QString &path)
{
s_path = path;
QSettings::setPath(format, scope, path);
}
QSettings *operator-> ()
{
return get_settings();
}
private:
QSettings *get_settings ()
{
if (!m_settings) {
if (s_path.isEmpty()) {
m_settings = std::make_unique<QSettings>();
} else {
m_settings = std::make_unique<QSettings>(s_path, QSettings::defaultFormat());
}
}
return m_settings.get();
}
static QString s_path;
std::unique_ptr<QSettings> m_settings;
};
QString settings::s_path = "";
When used with the default settings/values...
settings settings;
std::cout << "\nsettings file path is [" << settings->fileName() << "]";
The above give something like...
settings file path is [/home/<user>/.config/Unknown Organization/appname.conf]
Setting the desired path explicitly using...
settings::set_path(QSettings::defaultFormat(), QSettings::SystemScope, app.applicationDirPath() + "/settings.ini");
settings settings;
std::cout << "\nsettings file path is [" << settings->fileName() << "]";
gives...
settings file path is [/home/<user>/scratch/settings.ini]