I have a Qt application that requires the ability to load from several settings files to behave in a distinct way. For example lets say my app can support several variations, VAR1, VAR2, VAR3, ... One of my menu entries allows me to load settings. Currently, I do this using a QSettings object and it works fine. But now I want to implement a different variation. Instead of manually setting 20 or more settings, I would like to load from a preconfigured settings file.
QSettings does not allow me to change source, as far as I can tell. I looked at the static method QSettings::setPath but that can only be done once prior to instantiating the QSettings object.
My hope is to create ways to:
1 - Load factory defaults
2 - Save a user settings file with a user specified name
3 - Recall a user settings file by name.
I would prefer not to have to rewrite QSettings to meet my needs if at all possible.
One of the QSettings constructors takes a file name:
QSettings::QSettings (const QString& fileName,
Format format,
QObject *parent = 0);
Just use that to make as many QSettings instances as you need:
QSettings s1("path1.ini", QSettings::IniFormat);
QSettings s2("path2.ini", QSettings::IniFormat);
If you needed to copy between them you could look through QSettings::allKeys() and get the values.
Have you tried to access QSettings like an INI file ? It allows you to set up the location of the INI file
QSettings settings("/home/petra/misc/myapp.ini",
QSettings::IniFormat);
Related
Why it was possible in Qt 5.2 and previously and stored data in following format:
key=#Variant(\0\0\0\b\0\0\0)
but have problem now in Qt 5.11?! Following code
QVariantMap projectsMap;
for (auto project : projects)
projectsMap.insert(key, value);
settings->setValue("Group/projects", projectsMap);
executes correctly however stores nothing to ini file.
qRegisterMetaTypeStreamOperators<QVariantMap>("QVariantMap");
does not help as well. How to store this, what is the problem here?
Don’t store QSettings: it’s not meant to be used that way. You should use a fresh instance of QSettings every time you change the settings. Your destructor should look as follows:
MyClass::~MyClass() {
QSettings s;
s.setValue(kFoo, this->m_bar);
…
}
QSettings is an ephemeral handle to the settings system, its instantiation is cheap. You leak it because QPointer does not destroy anything: it’s not an owning pointer.
Saving UI settings with QSettings is cumbersome and buggy, because each time you must use setValue() and value() functions and also define groups, application name and organization which can be buggy in large applications:
QSettings settings(qApp->applicationDirPath() + "/" + qApp->applicationName() + ".ini" , QSettings::IniFormat) ...
settings.beginGroup("someGroup");
settings.setValue("someKey", "blah blah");
QString str = settings.value("someKey");
settings.endGroup();
However with JSON it can be simpler:
QJsonObject obj;
obj["someKey"] = "blah blah"
...
What is the best practice for saving and restoring ui settings?
Save each key/value in QSettings?
Save in QJson and then save with QSettings?
Save in QJson only (with another mechanism for defining groups and application)?
Any other idea?
The QSettings code won't be more cumbersome than your QJsonObject example if you use all benefits of the first one.
Default QSettings constructor:
You can set the application and organization names just once:
QApplication::setApplicationName("My Application");
QApplication::setOrganizationName("My Organization");
QSettings::setDefaultFormat(QSettings::IniFormat);
Then simply use the default QSettings constructor anywhere in your code:
QSettings settings;
settings.setValue("Key", "Value");
Group as an argument:
The settings group for your keys can be set without using the beginGroup() / endGroup() methods. Simply pass the slash-delimited argument to thevalue() / setValue() methods:
settings.setValue("Group/Key", "Value");
Storing the UI settings:
It's not clear from your question what exact UI settings you want to save, however there are two handy methods – QWidget::saveGeometry() and QMainWindow::saveState(). You can use it to store your windows geometry and state respectively:
QSettings settings;
settings.setValue("ui/geometry", saveGeometry());
settings.setValue("ui/state", saveState());
JSON:
In case if you still want some deep nesting and hierarchy for your settings file, you're right, you will have to use JSON. The most convenient way is to register the custom read/write functions using the QSettings::registerFormat. Why still QSettings? This class is designed considering the cross-platform code, no need to reinvent the wheel.
Of course, you can also write your own JSON settings class from scratch. But if there is no need in multilevel settings hierarchy – is it worth it?
In terms of application design you can wrap QSettings in an additional class. In this case you can easily experiment and switch to your own JSON read/write implementations without touching the main code.
Standard system paths:
In your example you are using the applicationDirPath() to store the settings data. That's an improper place to keep your settings for the most applications (e.g. you will likely face the Windows UAC issues in this case; Unix systems also have the separate directory for such files). Use the paths intended by the operating system for storing application data.
For example, on Windows QSettings::setDefaultFormat(QSettings::IniFormat) coupled with the default scope (UserScope) will store the settings in the %APPDATA% path. This also improves the cross-platform portability of your code.
What is an optimal and an appropriate way to save the state of a Qt GUI so I get the same state I had back when I closed the application ?
By state I mean : current indexes (for combo box ...), color palette, widgets positions... right before closing the application
You can use the QSettings class.
Simple use of QSettings class (code inspired from Qt's documentation):
In the main-window of your application code member functions that saves and restore the settings:
void MainWindow::writeSettings()
{
QSettings settings("reaffer Soft", "reafferApp");
settings.beginGroup("MainWindow");
settings.setValue("size", size());
settings.setValue("pos", pos());
settings.endGroup();
}
void MainWindow::readSettings()
{
QSettings settings("reaffer Soft", "reafferApp");
settings.beginGroup("MainWindow");
resize(settings.value("size", QSize(400, 400)).toSize());
move(settings.value("pos", QPoint(200, 200)).toPoint());
settings.endGroup();
}
Call those 2 functions from the MainWindow constructor and from the closeEvent override, like this:
MainWindow::MainWindow()
{
// code from constructor
//...
readSettings();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
//optional check if the user really want to quit
// and/or if the user want to save settings
writeSettings();
event->accept();
}
The direct answer requires specific elaborated design for your code and not really a short Qt question or even the question specific to Qt. That is about C++ which is not the VM-based language that assists with serializing the state of program code to data. Having all objects serializable we can then attempt to apply certain C++/Qt classes/techniques.
This task is much easier to accomplish with languages like Java, though. And with C++/Qt you have to routinely make serialize-able / serialize / restore everything that is running in your code and still no guarantee that works as long as the context is not fully captured. This task is not easy for sure and makes sense only in specific application.
The most you can get directly from Qt is to save/restore QMainWindow and other independent widgets geometry (position/size):
saveGeometry
restoreGeometry
... and that solution is still somewhat incomplete or you may/not use QSettings for the storage.
I use QSettings for this. With routines similar to Zlatomir's.
For each window I have in the project I use a different section in QSettings and have readSettings() and writeSettings() in the source for each window.
Anything on the form that I want to persist I have to explicitly save and recall. In the case of a QComboBox it would be something like:
QSettings settings("Organisation", "MySoftware");
settings.beginGroup("WindowNumberTwo");
settings.setValue("ComboIndex", combobox->currentIndex());
// save more values here
// ...
settings.endGroup();
I don't know of a built in way to persist your window states - it has to be don't value by value.
I read some QSettings from an .ini file:
QSettings* settingsDocRoot=new QSettings(_settingsFile ,QSettings::IniFormat, parent);
This is passed to some object. However, I then do a copy QSettings* s2= new QSettings(settingsDocRoot); and modify one particular value s2->setValue("path", whateverNewPath);
Basically I want to pass a slightly modified QSettings object to another object. But how do I avoid that the original ini file is updated with the changed value (s2->setValue)?
One idea was, simply to set the path to "". However, according to QSettings - where is the location of the ini file? then a default location will be assumed (OK, original file will not be changed, but unnecessary file will be written).
QSettings is entirely designed for persistence. If you don't want your copy to write to disk, you'd probably be better off copying all the values into a QHash and passing that to your other object:
QHash<QString, QVariant> hash;
const QStringList keys = settings->allKeys();
Q_FOREACH(QString key, keys) {
hash[key] = settings->value(key());
}
I am currently doing the following:
QSettings* settingsWsNaviGraph = new QSettings(settingsDocRoot);
// avoid writing to file
settingsWsNaviGraph->setPath(QSettings::InvalidFormat, QSettings::UserScope, "");
This dirty hack seems to avoid writing, at least my original file remains unchanged and I do not see any unwanted file yet (will report if I do find one).
If this here does not work, I'll try to register my own format with bogus read/write methods. See here
I'm developing a bunch of Qt applications in C++ and they all use some modules (translation units) for common functionality that use Qt as well.
Whenever I convert a C string (implicit conversion) or C++ string object (fromStdString()) to a QString object, I expect the original data to be UTF-8 encoded and vice versa (toStdString()).
Since the default is Latin-1, I have to set the codec "manually" (in the init procedure of every one of my programs) to UTF-8:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
Not all of my modules have an init procedure. The ones containing a class do (I can put this line in the class constructor), but some modules just contain a namespace with lots of functions. So there's no place for setCodecForCStrings(). Whenever I convert from/to a QString implicitly (from within one of my modules), I rely on the codec being already set by the init procedure of the main program, which seems to be a rather bad solution.
Is there a reliable way to set the codec to UTF-8 in my modules, or will I just have to be very careful not to use implicit conversions at all (in my modules at least) and write something like std::string(q.toUtf8().constData()) instead of q.toStdString()?
This can be done using a class definition for an automatically instantiated singleton-similar class having some init code in its constructor:
class ModuleInit {
static ModuleInit *instance;
public:
ModuleInit() {
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
}
};
ModuleInit * ModuleInit::instance = new ModuleInit(); // put this line in .cpp!
Putting this code anywhere into any project will set the text codec to UTF-8. You can, however, overwrite the text codec in the main() method, since the code above is executed even before the main() method.
With "anywhere" I of course mean places where it is syntactically allowed to. Put the class definition in some header file, and the instantiation in the appropriate .cpp file.
You can even put the whole code in one .cpp file, say moduleinit.cpp which you compile and link, but never explicitly use (there is no header file you could include...). This will also avoid accidental instantiations except the very first one and you will not have any problems with duplicate class names.
Note that you can't set the codec for C-strings in one particular file. Setting the codec using QTextCodec::setCodecForCString will set it application-wide!