Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Q_PROPERTY NOTIFY signal and its argument

I have the habit of writing my "propertyChanged" signals with an argument, such that the receiving end doesn't need to call the Q_PROPERTY's READ function explicitly.

I do this out of clarity and the 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.

My colleagues disagreed and said it was against "QML style", to which I responded the documentation clearly states it may have an argument that will take the new value of the underlying member:

NOTIFY signals for MEMBER variables must take zero or one parameter, which must be of the same type as the property. The parameter will take the new value of the property.

Nowhere in the documentation is it stated that the QML binding system uses this parameter to prevent an additional function call to the getter when handling the signal. I understand this call will probably be made from C++, so no "expensive" QML to C++ call will be made, but it still is an extra function call, which in principle could result in a visible performance penalty in case of many updates.

I tried inspecting the QML binding source code, but couldn't infer anything from it. I wonder if someone knows what the deal is: is the signal argument used or not?

like image 362
rubenvb Avatar asked Apr 30 '17 10:04

rubenvb


1 Answers

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
like image 58
Mitch Avatar answered Sep 29 '22 12:09

Mitch