Subclass/replace QML Settings
-
Hello,
For my fairly big QML application I have so far used
import Qt.labs.settings 1.0
to create a settings object inside QML that represents the persistent config for my application.Settings { id: pSettings property bool status: true property int foo: 5 }
Somewhere else in the application we then read or write
pSettings.foo = 9
. This works fine.However, I want to create a 'portable' version of my application. I want to write the configuration file to the path in which the application was started.
Currently it uses the registry on Windows, some XML stuff on MacOS and on Linux it writes to
~/.config/foo/bar.conf
.I've discovered that there is no way to change this behavior of the QML Settings component.
Solution 1
I can move this logic to C++ by using
QSettings
and passing it to QML. This however would result in a lot of code changes because we:- Read files via
var foo = pSettings.foo;
- Write values via
pSettings.foo = 3;
Which would need to get replaced by:
pSettings.setValue("4", 4);
pSettings.vaue("foo")
This is a lot of work to replace ... It changes our code for the worse and I like the non-intrusive interface that
Qt.labs.settings
provides.Solution 2
I've taken a look at the source code for Qt 5.9.7's QML settings component; it is in the following directory for me:
qtdeclarative-opensource-src-5.9.7/src/imports/settings
.Here is the github link
Is there a way to copy the contents of the
settings/
folder over to my project and change the code? I only need to change theQSettings
so that it writes to another path.More specifically I need to replace the function
QSettings *QQmlSettingsPrivate::instance() const {}
.I've tried and can't get it to work for several reasons. For one, the component is written as a QML plugin (with it's own individual
qmldir
and.pro
file) - I'm not sure how to integrate this into my existing qmake. Secondly, omitting those files and using the.cpp/.h
as-is yields some C++ errors likenote: forward declaration of ‘class QQmlSettingsPrivate’
. As I'm mostly a QML guy, I don't think I can make this approach work for me at this moment.Perhaps I can
#include
theQQmlSettingsPrivate
class somehow and subclass it, then overload the constructor so that when it instances theQSettings
it'll do so with a path that I can provide?Solution 3
Create our own
CoolSettings {}
QML component from scratch and register it viaqmlRegisterType<>
.My question here is .. Given this code:
CoolSettings { id: coolSettings property int bar: 5 }
This
bar
property was never made in the C++ implementation ofCoolSettings
. How can I dynamically add properties at runtime from QML?TL;DR question
How can I make
Qt.labs.settings 1.0
not write into Windows registry, not create XML files, just write to a 'normal' ini file in a path that I give it?Edit: I can see that Qt 5.12 added the ability to specify a filename. Upgrading to 5.12 is not possible unfortunately.
- Read files via
-
You can basically create a c++ source, and expose it later.
QString sfpath = QString("%1/%2").arg(QDir::currentPath()).arg("settings.ini"); this->general = new QSettings(sfpath,QSettings::IniFormat); this->general->beginGroup("pSettings"); this->general->setValue("status",true); this->general->setValue("foo",5); this->general->endGroup(); this->general->beginGroup("CoolSettings"); this->general->setValue("bar",5); this->general->endGroup();
This code is just to inspire you and will create a settings file "settings.ini" in the same path with your application. You can change the variables (group names, foo values and stuff) by the change of QML components. You do the math :D
-
Try the default format function to set it to ini or whatever you want. I was curious if something like this existed so I looked. Please let us know if this helps or not.
-
@fcarney said in Subclass/replace QML Settings:
Try the default format function to set it to ini or whatever you want. I was curious if something like this existed so I looked. Please let us know if this helps or not.
Thanks for the reply.
Even if this would work, I still have no control over where it is writing to.
I've started to backport
qml.labs.settings
from qt 5.12 directly into my program instead ;( -
@fcarney said in Subclass/replace QML Settings:
@RobertB said in Subclass/replace QML Settings:
no control over where it is writing to.
There is a setPath call as well. You can set this path for each major platform as well. Not sure this helps though.
Looking at QSettings::setPath, the signature is:
QSettings::setPath(Format format, Scope scope, const QString &path)
Where scope is either
QSettings::UserScope
orQSettings::SystemScope
.I've tried the following in my
main.cpp
, before I make aQQmlApplicationEngine engine
:QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, "/tmp/test.conf");
Then in QML:
import Qt.labs.settings 1.0 Settings { id: test property bool foo: true }
It defaults to my home directory instead (
~/.config/<org_name>/<proj_name>.conf
, so seems like this approach does not work. -
@J.Hilk said in Subclass/replace QML Settings:
hi @RobertB couple questions:
- is this
"/tmp/test.conf"
an absolute path ? - are you sure it exists
- why did you add the file to the path
- do you call the setPath function before your qml stuff is initialized?
/tmp/test.conf
is the absolute path I want a configuration file to be written to- I'm expecting QML to create this config file for me
- Because the parameter is called
path
, so I'm assuming the full path unless specified otherwise. - I call
setPath
before I init the QML engine.
- is this
-
@RobertB I was expecting it to not want to have a file name and ending. But I'm wrong.
Setpath doesn't seem to have an effect 🤔
QApplication a(argc, argv); QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QDir d; d.mkdir(path); QCoreApplication::setApplicationName("MyApp"); QCoreApplication::setOrganizationName("myOrganization"); QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, path); //QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, path + "settings.ini"); { QSettings s; s.setValue("Bla", "Blubb"); s.sync(); qDebug() << s.fileName(); } qDebug() << path;
returns
"/Users/jhilk/Library/Preferences/com.myorganization.MyApp.plist" "/Users/jhilk/Library/Application Support/MainWindowTest"
-
@J.Hilk said in Subclass/replace QML Settings:
@RobertB I was expecting it to not want to have a file name and ending. But I'm wrong.
Setpath doesn't seem to have an effect 🤔
QApplication a(argc, argv); QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QDir d; d.mkdir(path); QCoreApplication::setApplicationName("MyApp"); QCoreApplication::setOrganizationName("myOrganization"); QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, path); //QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, path + "settings.ini"); { QSettings s; s.setValue("Bla", "Blubb"); s.sync(); qDebug() << s.fileName(); } qDebug() << path;
returns
"/Users/jhilk/Library/Preferences/com.myorganization.MyApp.plist" "/Users/jhilk/Library/Application Support/MainWindowTest"
Yes, confusing.
Of course you could:
QSettings("/tmp/test.conf, QSettings::IniFormat, this)
Works as expected. But no way to pass it to
Qt.labs.settings 1.0
QML component (pre Qt 5.12.0).See also the difference between:
Qt 5.9.7 QML settings and Qt 5.12.0 QML settings
Where the Qt 5.9.7 is given no arguments (other than
this
) and in Qt 5.12.0 you can actually specify your ownfileName
.So for now I have backported this
QQmlSettings
class to Qt 5.9.7 (which BTW was a headache for someone with limited C++ capabilities).