Q_PROPERTY NOTIFY signal and its argument
assumption that in a QML data binding situation, no "expensive" call to the getter needs to be done to actually fetch the value, as it's already passed to QML as a signal argument.
Technically speaking, it is not likely that there is anything similar to what you describe. It just doesn't make sense. If your getters are expensive, you should take care to cache the result in a simpler form, and either update on changes or on demand.
If QML bindings were exclusively single property to single property, such an optimization could make sense. But bindings in QML are actually anonymous expressions, and the way it works is when any change notification for an object that is referenced by the expression triggers its reevaluation. In such a case it would add needless complexity to the implementation, having one value sent from the notification signal to keep and others which you have to call getters for.
Obviously, I am just postulating here. Another thing that makes me skeptic of the existence of such an optimization is that the property binding system isn't all that sophisticated or focused on efficiency. Which is evident from the fact that it cannot even eliminate redundant evaluations in the case of property value inter-dependencies. I've seen claims that such optimizations exist, but I've tested and it failed to avoid even the most simple of binding tree redundancies.
Naturally, if you insist on MEMBER
properties, this is not so easy to prove, since the getters are auto generated, and you don't get to insert your debug statement there.
But if you try it for a regular property, you will find out that even if the signal emits the actual value, the getter is invoked nonetheless. There is absolutely no reason why an auto-generated getter would get a different treatment.
As mentioned in the linked answer, if you need the value emitted for the C++ part of the code, then keep, it it will not interfere with the QML part. If not, then simply don't emit the value, even if minuscule, it is still overhead. Emitting the value is the C++ way, bindings are a fundamentally different concept that is not really applicable in C++ (not without extensive verbosity), the QML way does not require to emit the changed value.
So your are both wrong. It is not really "against the QML style", as it will not hinder anything, and having the option to emit a value in the documentation in no way suggest that it is "in line with the QML style", or that it gets any special treatment. So if that's the reason you are doing it, you might as well stop, as it is entirely redundant.
I wonder if someone knows what the deal is: is the signal argument used or not?
Here's one way to check:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
class MyType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool foo READ foo WRITE setFoo NOTIFY fooChanged)
public:
MyType(QObject *parent = nullptr) :
QObject(parent),
mFoo(0)
{
}
bool foo() const
{
qDebug() << Q_FUNC_INFO;
return mFoo;
}
void setFoo(bool foo)
{
if (foo == mFoo)
return;
mFoo = foo;
emit fooChanged(mFoo);
}
signals:
void fooChanged(bool foo);
private:
bool mFoo;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
qmlRegisterType<MyType>("App", 1, 0, "MyType");
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import App 1.0
Window {
width: 400
height: 400
visible: true
Switch {
id: fooSwitch
}
MyType {
id: myType
foo: fooSwitch.checked
onFooChanged: print("onFooChanged, foo =", foo)
// onFooChanged: print("onFooChanged myType.foo =", myType.foo)
}
}
The output when switching back and forth is:
qml: onFooChanged, foo = true
qml: onFooChanged, foo = false
So it's safe to say that the value is used and not the getter.
To see what the output would have been like if the signal argument hadn't been used, uncomment the commented out line and comment out the other one:
bool __cdecl MyType::foo(void) const
qml: onFooChanged myType.foo = true
bool __cdecl MyType::foo(void) const
qml: onFooChanged myType.foo = false
Passing the values of the changed properties in the onPropertyChanged
-signal is, though possible, most surely not the QML style.
Would it be, then you should expect that at least for the basic types it is implemented, which is easily shown, it's not.
basictypes.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: root
visible: true
width: 400; height: 450
property int num: 5
Button {
text: num
onClicked: num += 1
}
onNumChanged: console.log(JSON.stringify(arguments), arguments.length)
}
As you can see in the output, that there are no arguments passed, when you change even one of the most basic types, such as int
.
If now QML would use the optional, but rarely implemented passed value this would create overhead, as you would always need to check the existence of the argument before using it. Though a simple check is not to expensive, if it usually evaluates to false
, and then you use the workaround, why do it beforehand?
Though I might not rule out, that there are any passed values in any onPropertyChanged
-signals in the official realse, there are none for properties added in QML with property [type] [name]
. There also none for most inherited properties (tested the Button: text
, width
, height
).