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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With