Mary Chang
Mary Chang

Reputation: 955

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

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
        }
    }
}

Upvotes: 1

Views: 1022

Answers (3)

Vincent Fourmond
Vincent Fourmond

Reputation: 3288

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());
}

Upvotes: 2

Arun Kumar B
Arun Kumar B

Reputation: 311

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()
}

Upvotes: 0

Jakub Warchoł
Jakub Warchoł

Reputation: 76

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
        }
    }
}

Upvotes: 0

Related Questions