Save UI settings with QSettings or QJson? - qt

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.

Related

Why I can't store QVariantMap in QSettings?

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.

Application GUI state saving in Qt

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.

qt QTranslator reuse

I noticed that the Qt documentation is not very verbose on some of the aspects of the translations. I was fooling around with it trying to figure out their behaviour using trial & error. The ultimate goal is to get the translation changed on runtime but I am very confused as to what extent the QTranslator object can be re-used.
Consider this (where 'a' is the main instance of the application):
QTranslator translator;
translator.load("mytranslation_cz");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator)
Now the translator was removed from the application but what happened to the translator object?
In my tests when above code was followed by this again
translator.load("mytranslation_fr");
a.installTranslation(&translator);
it did not do anything in main() and it crashed the application when called from one of the widgets (using pointer to main app).
Therefore I am suspecting that I would need to create one QTranslator object per translation I want to load in the application and that I cannot reuse the QTranslator object. Am I right in this assumption?
And as a side question. Assuming the QTranslator object is untouched by the removeTranslation(), is it possible to simply install it later again like this?
QTranslator translator;
QTranslator translator1;
translator.load("mytranslation_cz");
translator1.load("mytranslation_fr");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator);
a.installTranslation(&translator1);
(...)
a.removeTranslation(&translator1);
a.installTranslation(&trasnlator); //Will this work?
Thanks for any clarification as I am somewhat confused as to what happens to the QTranslation objects when installing and removing translations from the application and especially if the QTranslation object can be reused for multiple translations somehow on runtime?
QTranslator::load basically in simple sense can be considered as a function that opens a given .qm file, reads in all the translated values and loads it in for a specific language.
Now in general operation you would not want to reuse this for many languages as by "reusing" (even if its allowed) your adding the overhead of parsing this given .qm file for every language every time you switch your UI language, which is just basically an overhead you don't need.
Your assumption of creating a QTranslator for each language is correct. As for your side question, Yes you can also re-use it. Thats the benefit of having individual QTranslator objects per translation. Just call qApp->removeTranslator() with the current translation and then qApp->installTranslator() with the new one. This way you are reusing the loaded translations as and when you please.
The way we structure this is by sub-classing QApplication and adding 2 functions
void Application::CreateTranslators() {
// translators_ is a QMap<QString, QTranslator*>
if (!translators_.isEmpty())
return;
QStringList languages;
languages << "en" << "ar" << "zh";
foreach(QString language, languages) {
QTranslator* translator = new QTranslator(instance());
translator->load(language);
translators_.insert(language, translator);
}
}
Now this function is called at the very start of the application.
2nd function is as following
void Application::SwitchLanguage(QString language) {
// current_translator_ is a QTranslator*
if (current_translator_)
removeTranslator(current_translator_);
current_translator_ = translators_.value(language, nullptr);
if (current_translator_)
installTranslator(current_translator_);
}
That's it. Using the second function you can switch your language at run-time as you please.
Couple things you'll also need to be aware of is changing QTranslator at run-time will update all translations from your .ui file strings marked as translatable automatically, however those set from code will not be. To get that you will have to override QWidget::changeEvent() and then check if the event is of type QEvent::LanguageChange and then set the required strings for that QWidget accordingly (Don't forget the tr() while doing so)

A clean and intuitive way to implement a list of clickable strings to invoke functions?

For the Qt App I'm writing, I'd like to have a list of clickable functions, which, when clicked will allow the user to supply required input arguments.
What I'm specifically looking for is a selection of widgets which provide a clean and intuitive interface for the following tasks:
User scrolls through a list of functions for performing computations (in my case, from glm).
Once a function is found, the user clicks on the item; a popup window opens, which specifies the required input arguments (e.g., vec3, vec4, etc.).
The idea here is that the functions themselves already exist: they just need an interface, which in a nutshell, provides a pseudo interpreter to process and output their results to a GLWidget, which will update the data passed accordingly by sending it to its corresponding shader.
I've looked at QListView, and its Widget variant, but it appears to be more suited towards filesystem data, such as images or text files, though I'm not quite sure. So far, it seems to be the only thing which could be considered as realistically usable in this scenario.
Is there a recommended way to do this? I'm fairly new to Qt in general, thus my knowledge is pretty limited.
The view isn't really important in your case. You need to create/reuse a adapted model.
This model have to contain the relation between what your view displays and the action that you want to launch.
For example, if your commands are text like bash commands, you can create a view that displays "list files", "Copy files" and a model that contains the data ("list files" = 'ls -l'), ("copy files" = 'ls -l'), etc.
You can store different data (using QVariant) in a same item with different roles: Qt::DisplayRole corresponds to the data that the view displays and Qt::UserRole what you want.
So, if you only have to store a command line associated to a name, you can store the name in the item with the Qt::DisplayRole and the command line as a QString (or other) using Qt::UserRole.
See QAbstractItemModel::data() and QAbstractItemModel::setData(), for more information.

How might I use QSettings to load different setups

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);

Resources