QML does not write to C++ property when bound to component and component value changes

0

I'm working on a QML project. In the UI I'm working on, I need to both update slider from C++ and read the current value into C++ from QML. Properties seems to be the right solution. So far I've read different questions on SO without success Two way binding C++ model in QML, Changed Properties do not trigger signal, etc... In my current code I declared a property in my C++ class

class MyClass : public QObject {
    Q_OBJECT
public:
    MyClass(QObject*);
    Q_PROPERTY(double myValue READ getMyValue WRITE setMyValue NOTIFY myValueChanged)

    void setMyValue(double n) {
        std::cerr << "myValue  being update: " << n << "\n";
        myValue = n;
    }

    double myValue = 30;
...
}

And exposed it into Qt via a singleton

qmlRegisterSingletonInstance("com.me.test", 1, 0, "MyClass", &myClass);

Then bound the C++ property to a QML slider

import com.me.test
ApplicationWindow {
    Slider {
        id: slider
        height: 30
        width: 100
        from: 0
        to: 100
        value: myClass.myValue
        onValueChanged {
            console.log("value = " + value)
            console.log("myClass.myValue = " + myClass.myValue)
        }

        /* Doesn't help
        Binding {
            target: slider
            property: "value"
            value: myClass.myValue
        }*/
    }
}

The binding seems to work. I can modify the value of myValue then emit myValueChanged to make QML update it's slider. But given that myClass.myValue is bounded to slider.value. I'd assume both values gets updated at the same time. But dragging the slider shows that they have different values. The following it what is printed in the console when I drag my slider.

qml: value = 19.863013698630137
qml: myClass.myValue = 30

Furthermore setMyValue seems to not being called unless an explicit assignment is made like myClass.myValue = 0. I also tried the Binding component without success. Why is this the case and could I make the C++ property updated whenever i drag the slider?

Qt: 6.2.1
Compiler: clang/gcc
OS: Windows/Linux

Update: tested a reverse binding. Still printing the same result

import com.me.test
ApplicationWindow {
    Slider {
        id: slider
        height: 30
        width: 100
        from: 0
        to: 100
        value: myClass.myValue
        onValueChanged {
            console.log("value = " + value)
            console.log("myClass.myValue = " + myClass.myValue)
        }
        Binding {
            target: myClass
            property: "myValue"
            value: slider.value
        }
    }
}
c++ qml qt qt6
2021-11-24 06:49:49
3
0

To update C++ property from QML, you can use Binding:

import com.me.test
ApplicationWindow {
    Slider {
        id: slider
        height: 30
        width: 100
        from: 0
        to: 100
        value: myClass.myValue
        onValueChanged {
            console.log("value = " + value)
            console.log("myClass.myValue = " + myClass.myValue)
        }
         // C++ property was bounded to QML above, now we should bind QML to C++
        Binding {
            target: myClass 
            property: "myValue"
            value: slider.value
        }
    }
}
2021-11-24 09:12:14

what happens when myClass.myValue will be updated and will fire its myValueChanged?
folibis

@folibis It will update the slider value' property. Of course, the setMyValue method should start with comparison between a new value and the current value, and emit myValueChanged only if the value was really updated. In other case, you could see a binding loop
Jakub Warchoł

Thanks for the reply. I tried the suggested solution. But still not seeing the setter called nor the value of console.log("myClass.myValue = " + myClass.myValue) changed.
Mary Chang

@MaryChang In the above code, you will see console.log("myClass.myValue = " + myClass.myValue) when you will move a slider. To see myClass.myValue value changed, have a look at Connections
Jakub Warchoł
0

Here's a minimal working example of two way updating Slider:

main.cpp:

data dataObj;
engine.rootContext()->setContextProperty("dataCpp", (QObject*)&dataObj);

data.h:

class data : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
    explicit data(QObject *parent = nullptr);
    int value(void);
    void setValue(int new_value);
public slots:
    void reset(void);
signals:
    void valueChanged();
private:
    int dataValue;
};

data.cpp:

data::data(QObject *parent) : QObject(parent)
{
    dataValue = 250;
}

int data::value()
{
    return dataValue;
}

void data::setValue(int new_value)
{
    if(dataValue != new_value)
    {
        qDebug() << "setting" << new_value;
        dataValue = new_value;
        emit valueChanged();
    }
}

void data::reset()
{
    if(dataValue != 0)
    {
        qDebug() << "resetting to 0";
        dataValue = 0;
        emit valueChanged();
    }
}

main.qml:

Slider {
    id: slider
    height: 50
    width: 500
    from: 0
    to: 500
    live: false
    value: dataCpp.value
    onValueChanged: dataCpp.value = value
}

Button{
    anchors.top: slider.bottom
    text: "Reset"
    onPressed: dataCpp.reset()
}
2021-12-14 13:21:29
0

You're missing the signal when modifying the value:

Q_PROPERTY(double myValue READ getMyValue WRITE setMyValue NOTIFY myValueChanged)

This means that you promise you'll send the myValueChanged signal whenever the C++ code changes the value. So the following should work:

void setMyValue(double n) {
    std::cerr << "myValue  being update: " << n << "\n";
    myValue = n;
    emit(myValueChanged());
}
2021-12-14 15:18:53

In other languages

This page is in other languages

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................