Access QML elements from C++ classes



  • Hello,

    I would like to access all my QML elements from a C++ class wich is not my "main.cpp". I found some examples on the web but only on the "main.cpp". I want to access it from a custom C++ class.

    How can I do that ?

    I'm trying to handle properties from a file that will change value or state of differents QML components. I am able to read property from my file and change the value of the QML components but I'm not able to save the state of the components in the properties file.



  • @DavidM29
    Can you show me an example of how are you trying to do that ?

    Because depending of your implemention, you can expose a c++ object to qml and acess the proprieties of qml components using this object.



  • For example I have a two state option with a label it look like that :

    0_1529410500784_0504c349-7dc1-455c-b3bf-b7a022478e77-image.png

    This is a Component to me wich I called OptionSwitch. My OptionSwitch has 2 properties :

    • The label of the option wich is a String
    • The switch wich is an alias in order to access it states

    From my option page I have implement a Component a FileIO wich is my C++ class to read my text file.
    In my main.cpp I added this line to give access to my FileIO as a component in QML :

        qmlRegisterType<FileIO, 1>("FileIO", 1, 0, "FileIO");
    

    In my QML I just have to set the location of my file and then I access to the value of by calling FileIO functions.

    This is just for one option but I want to handle it in a more generic way. Here is an example of what I want to do :

    Example of properties file :
    "element.property01=value01
    element.property02=value02
    element2.property01=value03
    "

    The QML option page would contains many OptionSwitch for example :

    OptionSwitch{
          id: element
          property01: value01
          property02: value02
    }
    OptionSwitch {
          id: element2
          property01=value03
    }
    

    When I load my QML element I read the value from my file wich works at the moment.
    Then to save I would like to use a button that call my C++ write function.

    When I read my value from my file I use a map of strings where the Key is the name of the property and the value is the value of the property.

    I'm not sure to be very clear on everything, so if I'm not or you want more information please ask me.



  • @DavidM29

    Okay, you could write and read information from .json file but after few time googling, i noticed that QT have a class to work with set and get of properties of application.

    QSettings C++ Documentation

    You could to use the QSettings Class to set and get values of .ini file in your application. But QSettings is a c++ class, so you need to create a custom class and reimplement the methods to be callable from QML.

      1. Create a setting class derived of QSettings
      1. Reimplement the basic methods: setValue, value and construtors
      1. Create a object of custom settings and expose to context of qml

    Below is an example of how you can implement:

    customsettings.h file

    #ifndef CUSTOMSETTINGS_H
    #define CUSTOMSETTINGS_H
    
    #include <QObject>
    #include <QSettings>
    #include <QVariant>
    
    class CustomSettings : public QSettings
    {
        Q_OBJECT
    public:
        CustomSettings(QObject *parent = nullptr);
        CustomSettings(const QString &fileName, QSettings::Format format, QObject *parent = nullptr);
        Q_INVOKABLE void setValue(const QString &key, const QVariant &value);
        Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
    
        Q_INVOKABLE void sync();
    };
    
    #endif // CUSTOMSETTINGS_H
    
    

    customsettings.cpp file

    #include "customsettings.h"
    
    CustomSettings::CustomSettings(QObject *parent) : QSettings(parent)
    {
    
    }
    
    CustomSettings::CustomSettings(const QString &fileName, QSettings::Format format, QObject *parent) : QSettings(fileName, format, parent)
    {
    
    }
    
    void CustomSettings::setValue(const QString &key, const QVariant &value)
    {
        QSettings::setValue(key, value);
    }
    
    QVariant CustomSettings::value(const QString &key, const QVariant &defaultValue) const
    {
        return QSettings::value(key, defaultValue);
    }
    
    void CustomSettings::sync()
    {
        QSettings::sync();
    }
    

    main.cpp file

    #include <QApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include "customsettings.h"
    
    int main(int argc, char *argv[]) {
        QApplication app(argc, argv);
    
        QCoreApplication::setOrganizationName("YourOrganization");
        QCoreApplication::setOrganizationDomain("yourorganization.com");
        QCoreApplication::setApplicationName("Your Application Name");
    
        CustomSettings settings("mysettings.ini", QSettings::IniFormat);
    
        QQmlApplicationEngine engine;
    
        engine.rootContext()->setContextProperty("settings", &settings);
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    

    main.qml file

    import QtQuick 2.4
    import QtQuick.Window 2.1
    import QtQuick.Layouts 1.2
    import QtQuick.Controls 2.0
    
    Window {
        id: window
        width: 800
        height: 600
        minimumHeight: 350
        minimumWidth: 500
        visible: true
        color: "#222222"
    
        function saveSettings(){
            settings.setValue("nametextfield/text", nameTextField.text);
            settings.setValue("switch01/checked", switch01.checked);
            settings.setValue("switch02/checked", switch01.checked);
    
            settings.sync(); // necessary to force the saving as permanenty data
        }
    
        function loadSettings(){
            nameTextField.text = settings.value("nametextfield/text", nameTextField.text);
            switch01.checked = settings.value("switch01/checked", switch01.checked);
            switch02.checked = settings.value("switch02/checked", switch02.checked);
        }
    
        ColumnLayout{
            width: 450
            height: 250
            anchors.centerIn: parent
    
            RowLayout{
                Item{ Layout.fillWidth: true }
    
                Text {
                    text: "Name: "
                    color: "white"
                }
                TextField{
                    id: nameTextField
                    text: "Insert Your Name"
                }
    
                Item{ Layout.fillWidth: true }
            }
    
            Item{  Layout.fillHeight: true }
    
            RowLayout{
                Item{ Layout.fillWidth: true }
    
                Text {
                    text: "Switch 01: "
                    color: "white"
                }
                Switch {
                    id: switch01
                    checked: false
                }
    
                Item{ Layout.fillWidth: true }
            }
    
            RowLayout{
                Item{ Layout.fillWidth: true }
    
                Text {
                    text: "Switch 02: "
                    color: "white"
                }
                Switch {
                    id: switch02
                    checked: false
                }
    
                Item{ Layout.fillWidth: true }
            }
    
            Item{ Layout.fillHeight: true }
    
            RowLayout{
                Item{ Layout.fillWidth: true  }
    
                Button{
                    id: saveButton
                    text : "Save Data"
                    onClicked: window.saveSettings(); // call Save Function
    
                }
    
                Button{
                    id: loadButton
                    text: "Load Data"
    
                    onClicked: window.loadSettings(); // call Load Function
                }
    
                Button{
                    id: reset
                    text: "Reset Data"
                    onClicked: {
                        switch01.checked = false
                        switch02.checked = false
                        nameTextField.text = "Insert Your Name"
                    }
                }
    
                Item{ Layout.fillWidth: true }
            }
        }
    }
    




  • @KillerSmath
    Thank you for your answer it is a good solution. Almost everything works fine !
    I have only one issue. When I load my settings I do it like that in my QML :

        
        Component.onCompleted: {
            loadSettings()
        }
    

    But when I do that the value it reads is good but it does not apply it to my component. Here is my loadSettings method :

       function loadSettings(){
            console.log(settings.value("Gen/night", night.checked))
            night.checked = settings.value("Gen/night", night.checked)
            console.log(night.checked)
        }
    

    When I launch it the console print : "qml: false" at first so the read value is false (wich is the expected value) then it is supposed to change the state of my component. But it keep its value at true. The console show "qml:true" on the second console.log

    I don't really understand why it behave like that, do you have any clues ?



  • @DavidM29
    This is a problem of convertion between the set and get of data.

    When you use settings.setValue("Name", bool), this boolean is saved as String in .ini file but when you recover this value and tries to convert to bool again, occours a conversion error.

    When you use settings.setValue("Name", bool), this boolean is saved as String in .ini file but when you try to recover, this value is returned as Qvariant of string and because it, occours a conversion error.

    setValue()   ->      value()
    bool         ->      "bool"
    false        ->      "false"
    132442       ->      "132442"
    "121415"     ->      "121415"
    

    Javascript language implements some of ECMA-262 specifications

    http://www.ecma-international.org/ecma-262/5.1/#sec-9.2

    Notice that:

    The result is false if the argument is the empty String (its length is zero); otherwise the result is true.

    Settings QML Type already implement a feature to solve this problem but with limitation.

    Settings QML Type Notes:

    The current implementation is based on QSettings. This imposes certain limitations, such as missing change notifications. Writing a setting value using one instance of Settings does not update the value in another Settings instance, even if they are referring to the same setting in the same category.

    So, if this limitation is a problem for your application, you can implement a feature to fix this problem in c++ QSettings.

    QVariant CustomSettings::value(const QString &key, const QVariant &defaultValue)
    {
        QVariant value = QSettings::value(key, defaultValue);
        
        if (QString(value.typeName()) == "QString" &&  (value.toString() == "false" || value.toString() == "true"))
            return QVariant(value.toBool());
        
        return value;
    }
    

    Extra: if you need to save settings and load in all instances of your application, i recommend you to implement another method of storage as .json that can difference true boolean of "true" string

    References:
    QML engine not implicitly converting a bool-string QVarient to bool property - StackOverFlow



  • If I wan't to implement another method of storage I will not be able to use QSettings anymore as long as it does not support .json format. Am I wrong ?



  • @DavidM29 the problem is not the file type but the Json Sintaxe



  • Yes I understand because in .json the string value are encapsulated in " " but to do that I will have to make my own json reader and writer I will not be able to use the QSettings.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.