Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 the QSettings 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 like note: 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 the QQmlSettingsPrivate class somehow and subclass it, then overload the constructor so that when it instances the QSettings it'll do so with a path that I can provide?

    Solution 3

    Create our own CoolSettings {} QML component from scratch and register it via qmlRegisterType<>.

    My question here is .. Given this code:

    CoolSettings {
      id: coolSettings
      property int bar: 5
    }
    

    This bar property was never made in the C++ implementation of CoolSettings. 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.



  • 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 ;(



  • @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.



  • @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 or QSettings::SystemScope.

    I've tried the following in my main.cpp, before I make a QQmlApplicationEngine 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.


  • Moderators

    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?


  • @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.

  • Moderators

    @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 own fileName.

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


Log in to reply