creating QML signals for C++ slots
-
@mzimmers said in creating QML signals for C++ slots:
How (if at all) does it change the behavior of the program?
It is how everything in QML gets notified when a value changes. So if you did the following:
Text { text: sp.value }
If the signal is not in the property definition "text" will never get updated. Every binding to every property in QML works this way.
-
@mzimmers If the value is not changed it won't fire. The initial value will be read at some point. Then it will only fire if it changes.
I am thinking you want some other behavior in your code. What do you want to happen? Ignore the code. Are you wanting to log data periodically?
-
@fcarney said in creating QML signals for C++ slots:
@mzimmers If the value is not changed it won't fire. The initial value will be read at some point. Then it will only fire if it changes.
But when I manipulate the slider through the GUI, this signal never seems to fire. I do get the call to the setValue() function.
I am thinking you want some other behavior in your code. What do you want to happen? Ignore the code. Are you wanting to log data periodically?
No, I'm more just trying to remind myself/better understand how this mechanism works. I'll be using a lot of it in this upcoming project, and I want to get it down cold. I'm still confused on what causes the property signal to fire, as in my example, it doesn't happen merely by changing the value of the property.
-
main.cpp:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <QDebug> #include "cppobject.h" int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif QGuiApplication app(argc, argv); CppObject cppobject; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("cppObject", &cppobject); // some connection to some object cppobject.connect(&cppobject, &CppObject::valueChanged, [](float value){ qDebug() << "cppObject.value changed:" << value; }); const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
main.qml:
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 Window { width: 640 height: 480 visible: true title: qsTr("Hello World") ColumnLayout { Slider { Layout.minimumWidth: 500 Layout.minimumHeight: 50 value: cppObject.value onValueChanged: { cppObject.value = value } } Text { text: cppObject.value } } }
cppobject.h:
#ifndef CPPOBJECT_H #define CPPOBJECT_H #include <QObject> class CppObject : public QObject { Q_OBJECT Q_PROPERTY(float value READ value WRITE setValue NOTIFY valueChanged) float m_value; public: CppObject() : m_value(0) {} float value() const; void setValue(float newValue); signals: void valueChanged(float value); }; inline float CppObject::value() const { return m_value; } inline void CppObject::setValue(float newValue) { if (qFuzzyCompare(m_value, newValue)) return; m_value = newValue; emit valueChanged(newValue); } #endif // CPPOBJECT_H
You can see the affect without the signal by running with it commented out.
For demonstration I hooked a lambda to the signal of cppobject in main.cpp.
This was tested in Qt 5.15.2. -
@fcarney thanks for the code sample. I didn't try it, because I'm sure it works.
I'm still not getting something: why does setValue have to emit the signal if the NOTIFY property is supposed to cause this to happen automatically when the value changes?
-
Here is the issue: the NOTIFY keyword is a hint for the property system. It is still your duty to emit it at the adequate time.
-
@mzimmers NOTIFY tells other objects what signal fires when the property changes. It does not actually fire the signal.
So in QML if the cppObject.value is bound to the "text" property of Text, the code attaches to the signal specified by the NOTIFY part of Q_PROPERTY. If the NOTIFY is not specified, it doesn't know what to attach to. You will also get warnings saying the property is non-notifiable. There is a lot going on in QML property bindings.
It is also possible for multiple Q_PROPERTY's to use the same signal if you want them all to update when one of them changes. This might be useful to sync updates.
-
@SGaist @fcarney thank you for the distinction; that makes sense now.
So, am I correct that it's best to minimize the "work" done in the QML (in my case, with this line)
onReleased: sp.value = control.value
And let the C++ code handle the rest?
And, as I mentioned earlier, this line is my only needed modification to a third-party module. Is there some way to "inject" this behavior from the QML that uses this Item?
Thanks...