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 :
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.
-
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.
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.
-
- Create a setting class derived of QSettings
-
- Reimplement the basic methods: setValue, value and construtors
-
- 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 -
@DavidM29 the problem is not the file type but the Json Sintaxe
-
@KillerSmath Thank you so much! Very useful answer.