Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to re-evaluate c++ function bound to qml property on dependent property change?

I want to bind a method of a C++ class to a QML component property and re-evaluate it when a dependent property changes. The following QML component does what I want:

// Converter.qml:
import QtQuick 2.0

QtObject {
    property double rate: 1
    function exchange(amount) { return amount * rate }
}

If I assign the result of the exchange function to a property like so,

Text { text: converter.exchange(100) }

the Text element will automatically update when rate is changed. This works with a QML component, but I do not know how to do it with a C++ class.

I would like to implement a functionally equivalent class in C++:

#include <QObject>

class Convert : public QObject
{
    Q_OBJECT
    Q_PROPERTY(double rate READ rate WRITE setRate NOTIFY rateChanged)

public:
    explicit Convert(QObject *parent = 0)
        : QObject(parent), m_rate(1.0)
    { }

signals:
    void rateChanged();

public slots:
    double exchange(double amount) { return m_rate * amount; }

    double rate() { return m_rate; }

    void setRate(double r) {
        m_rate = r;
        emit rateChanged();
    }

private:
    double m_rate;
};

The rate property is accessible from QML, but changing it does not signal to the QML engine that 'exchange' should be re-evaluated.

Here is my main.qml:

// main.qml
import QtQuick 2.1
import QtQuick.Controls 1.1
import Utils 1.0

ApplicationWindow {
    width: 300; height: 200
    visible: true

//    Converter { id: converter; rate: rateField.text }
    CppConverter { id: converter; rate: rateField.text }
    Column {
        TextField { id: rateInput; text: '0.41' }
        TextField { id: amountInput; text: '100.00' }
        Text { id: output; text: converter.exchange(amountField.text).toFixed(2) }
    }
}

If I enable the CppConverter, the output is updated when I change the amountInput, but not when I change the rateInput. If I comment-in the QML Converter element, the update works fine.

With the QML Converter, the QML runtime identifies the dependency on the rate property and re-evaluates the exchange function when the rate is changed. How can I indicate to the QmlEngine to do the same in the C++ version?

like image 508
pixelgrease Avatar asked Oct 20 '22 00:10

pixelgrease


1 Answers

There's no way to do this currently.

I don't think relying on a function being re-evaluated like this is a good practice, though. You should either explicitly call it when necessary:

Connections {
    target: converter
    onRateChanged: output.text = converter.exchange(amountField.text)
}

Or convert exchange() into a property, and approach it declaratively instead of imperatively (code not complete or tested):

class Convert : public QObject
{
    // ...
    Q_PROPERTY(double amount READ amount WRITE setAmount NOTIFY amountChanged)
    Q_PROPERTY(double exchange READ exchange NOTIFY exchangeChanged)

    // ...

public:
    double exchange() { return m_rate * m_amount; }

private:
    double m_rate;
    double m_amount;
};

You'd then need to emit the various *Changed signals, etc. in the appropriate places.

like image 82
Mitch Avatar answered Nov 01 '22 13:11

Mitch