Reputation: 1147
I read from an .ini file into an integer in c++, which is then utilised in QML through binding. In runtime, the value in the .ini file can be changed, which causes the c++ integer to also be changed. I'm finding that although the integer does indeed change in c++ (verified through qDebug()), the bound value in QML doesn't change, despite emitting the required changed() signal. Excerpts of my application structure are shown below:
main.cpp:
//Establish the QQmlApplication engine, set context properties for the two c++ files and load the QML file.
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("MainCpp"), new MainCpp());
engine.rootContext()->setContextProperty(QStringLiteral("Config"), new Config());
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
maincpp.h:
public:
explicit MainCpp(QObject *parent = nullptr);
Q_PROPERTY(int someValue READ someValue NOTIFY someValueChanged)
int someValue(){return m_someValue;}
signals:
void someValueChanged();
private:
int m_someValue;
config.h:
public:
explicit Config(QObject *parent = nullptr);
Q_PROPERTY(int someOtherValue READ someOtherValue NOTIFY someOtheralueChanged)
int someOtherValue(){return m_someOtherValue;}
signals:
void someOtherValueChanged();
public:
void loadSettings();
public:
int m_someOtherValue;
QString m_File;
config.cpp:
Config::Config(QObject *parent) : QObject(parent)
{
m_File = "/some/path/to/settings/file/config.ini";
loadSettings();
}
void Config::loadSettings()
{
QSettings settings(m_File, QSettings::IniFormat);
settings.beginGroup("GROUP_NAME");
m_someOtherValue = settings.value("someOtherValueConfig", "").toInt();
settings.endGroup();
}
maincpp.cpp:
MainCpp::MainCpp(QObject *parent = nullptr) : QObject(parent)
{
Config configPointer;
m_someValue = configPointer.someOtherValue();
emit someValueChanged();
}
main.qml:
Window {
width: 800
height: 480
Text {
id: someText
text: Config.someOtherValue //This does NOT update on changes to m_someOtherValue on the c++ side
//text: MainCpp.someValue //This DOES update on change to m_someValue on the c++ side
}
}
The following code is called within maincpp.cpp to update the .ini file:
void MainCpp::update(int var)
{
Config configPointer;
QSettings settings("/some/path/to/settings/file/config.ini", QSettings::IniFormat);
settings.setValue("GROUP_NAME/someOtherValueConfig", var);
configPointer.m_someOtherValue = var;
m_someValue = configPointer.someOtherValue;
emit configPointer.someOtherValueChanged();
emit someValueChanged();
}
I've added 'emit someOtherValueChanged()' signals to no avail. As mentioned earlier, I know m_someOtherValue has changed, as I use query it with qDebug(). If m_someValue changes, why doesn't QML observe the change in m_someOtherValue?
Upvotes: 0
Views: 1831
Reputation: 243993
The behavior is caused because you have 3 Config objects:
engine.rootContext()->setContextProperty(QStringLiteral("Config"), new Config());
MainCpp::MainCpp(QObject *parent = nullptr) : QObject(parent)
{
Config configPointer;
[...]
void MainCpp::update(int var)
{
Config configPointer;
That is to say if changes some of them does not change the other objects since they are different.
A possible solution is to make Config a Singleton, so all the objects will be the same throughout the application.
config.h
#ifndef CONFIG_H
#define CONFIG_H
#include <QObject>
#include <QSettings>
class Config : public QObject
{
static Config* instance;
Q_OBJECT
Q_PROPERTY(int someOtherValue READ someOtherValue NOTIFY someOtherValueChanged)
explicit Config(QObject *parent = nullptr);
public:
static Config *getInstance();
int someOtherValue(){return m_someOtherValue;}
[...]
};
#endif // CONFIG_H
config.cpp
#include "config.h"
Config* Config::instance = 0;
Config::Config(QObject *parent):QObject(parent){
m_File = "/some/path/to/settings/file/config.ini";
loadSettings();
}
Config *Config::getInstance(){
if (instance == 0)
instance = new Config;
return instance;
}
void Config::loadSettings(){
[...]
}
The object is then accessed through getInstance ():
maincpp.cpp
#include "maincpp.h"
MainCpp::MainCpp(QObject *parent):QObject(parent){
Config *configPointer = Config::getInstance();
m_someValue = configPointer->someOtherValue();
emit someValueChanged();
}
void MainCpp::update(int var)
{
Config *configPointer = Config::getInstance();
QSettings settings("/some/path/to/settings/file/config.ini", QSettings::IniFormat);
settings.setValue("GROUP_NAME/someOtherValueConfig", var);
configPointer->m_someOtherValue = var;
m_someValue = configPointer->someOtherValue();
emit configPointer->someOtherValueChanged();
emit someValueChanged();
}
To use it in QML you must register with the help of qmlRegisterSingletonType()
:
main.cpp
#include "maincpp.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
static QObject *singletonTypeProvider(QQmlEngine *, QJSEngine *)
{
return Config::getInstance();
}
int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<Config>("Config", 1, 0, "Config", singletonTypeProvider);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("MainCpp"), new MainCpp());
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
In the qml you must import the module and use the object:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import Config 1.0
Window {
width: 800
height: 480
visible: true
ColumnLayout{
Text {
id: someText
text: Config.someOtherValue
}
Text {
id: anotherText
text: MainCpp.someValue
}
Slider {
value: 0.5
maximumValue: 100
onValueChanged: MainCpp.update(value)
}
}
}
The complete example can be found in the following link.
Upvotes: 1