Signal not transported from QML to cpp?
-
Hi,
I have a very strange behaviour. I have exposed a CPP-Object to QML and in QML I change the value of a QString-Property.
On CPP-Side I am connected to the notify-signal for that property.Expected behaviour:
CPP-Side gets notified for the value-change.Actual behaviour:
CPP-Side is not being notified about value-change.Strange: In a small Test-Programm with CPP-Only it works.
I found something in the "activate"-Function of QMetaObject which I dont understand:
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv) { int signal_index = signalOffset + local_signal_index; if (sender->d_func()->blockSig) return; if (sender->d_func()->isDeclarativeSignalConnected(signal_index) && QAbstractDeclarativeData::signalEmitted) { QAbstractDeclarativeData::signalEmitted(sender->d_func()->declarativeData, sender, signal_index, argv); } if (!sender->d_func()->isSignalConnected(signal_index, /*checkDeclarative =*/ false) && !qt_signal_spy_callback_set.signal_begin_callback && !qt_signal_spy_callback_set.signal_end_callback) { // The possible declarative connection is done, and nothing else is connected, so: return; }
The last if, see qobject.cpp line 3621, sorts out the notification. Means: we are leaving the activate-function with the last return in the above code snippet. Declarative Stuff seems to get notified, some lines before...
Anyone has an idea, why cpp-signalling is skipped?
Best Regards,
Tobias -
Hi,
I have a very strange behaviour. I have exposed a CPP-Object to QML and in QML I change the value of a QString-Property.
On CPP-Side I am connected to the notify-signal for that property.Expected behaviour:
CPP-Side gets notified for the value-change.Actual behaviour:
CPP-Side is not being notified about value-change.Strange: In a small Test-Programm with CPP-Only it works.
I found something in the "activate"-Function of QMetaObject which I dont understand:
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv) { int signal_index = signalOffset + local_signal_index; if (sender->d_func()->blockSig) return; if (sender->d_func()->isDeclarativeSignalConnected(signal_index) && QAbstractDeclarativeData::signalEmitted) { QAbstractDeclarativeData::signalEmitted(sender->d_func()->declarativeData, sender, signal_index, argv); } if (!sender->d_func()->isSignalConnected(signal_index, /*checkDeclarative =*/ false) && !qt_signal_spy_callback_set.signal_begin_callback && !qt_signal_spy_callback_set.signal_end_callback) { // The possible declarative connection is done, and nothing else is connected, so: return; }
The last if, see qobject.cpp line 3621, sorts out the notification. Means: we are leaving the activate-function with the last return in the above code snippet. Declarative Stuff seems to get notified, some lines before...
Anyone has an idea, why cpp-signalling is skipped?
Best Regards,
Tobias@topse
Just to make sure: Are you triggering the signal whenever the value changes? So simply specifying the NOTIFY signal isn't enough. That just tells the meta-object system what the corresponding signal is. -
@topse
Just to make sure: Are you triggering the signal whenever the value changes? So simply specifying the NOTIFY signal isn't enough. That just tells the meta-object system what the corresponding signal is.@raven-worx
yeah... I know :-) When changing the value in the set-Function, the signal is called... -
@topse
Just to make sure: Are you triggering the signal whenever the value changes? So simply specifying the NOTIFY signal isn't enough. That just tells the meta-object system what the corresponding signal is.@raven-worx offtop - should I manually bind NOTIFY signal with some slot in QML file if I want continuous refreshing of displaying value whenever and wherever it changed? In my application I need to change some variable in a context object attached to main.qml and the result must be displayed immediately and (it's desirable) automatically after evaluation on the context object side without any hand work like it's done in classic Qt Widgets application by overriding event handlers.
-
@raven-worx offtop - should I manually bind NOTIFY signal with some slot in QML file if I want continuous refreshing of displaying value whenever and wherever it changed? In my application I need to change some variable in a context object attached to main.qml and the result must be displayed immediately and (it's desirable) automatically after evaluation on the context object side without any hand work like it's done in classic Qt Widgets application by overriding event handlers.
@Tikani
well the most effortless way is to use QML's property binding feature -
@raven-worx
yeah... I know :-) When changing the value in the set-Function, the signal is called...@topse
please show some more code, maybe there can be spotted more -
@topse
please show some more code, maybe there can be spotted more@raven-worx
To be honest, its a bit complicated to give more code... First there is a CPP-Type passed to QML and instantiated there, this type has a QObject which has the property... I will have a look, what I can do to get that in short... -
@topse
please show some more code, maybe there can be spotted moreHi Raven,
situation gets more complicated... I try it here:In QML we have a view:
import QtQuick 2.7 import com.mycompany.qmlcomponents 1.0 Page{ A { id: ctrl } textFieldNachname { onTextChanged: ctrl.card.nachname = text; } testButton { onClicked: ctrl.testfct(); } }
Class A we have here:
class A: public QObject { Q_OBJECT Q_PROPERTY(JCard* card READ card CONSTANT) private: JCard *_card; public: JCard *card() const; Q_INVOKABLE void testfct(); private slots: void slotTest(); } A::A(QObject *parent) : QObject(parent) { _card = new JCard(this); bool success; success = connect(_card, SIGNAL(nachnameChanged()), SLOT(slotTest()) ); Q_ASSERT(success); } void A::testfct() { qDebug() << "testfct: " << _card->get_nachname(); _card->nachnameChanged(); } void A::slotTest() { qDebug() << "slotTest: Nachname changed to " << _card->get_nachname(); }
And JCard:
class JCard : public QObject { Q_OBJECT // Creates a property with setters and getters and notification QML_WRITABLE_AUTO_PROPERTY(QString, nachname) public: JCard(QObject *parent = Q_NULLPTR); };
Class A and JCard are given to QML:
qmlRegisterType<A> ("com.mycompany.qmlcomponents", 1, 0, "A"); qmlRegisterType<JCard>("com.mycompany.qmlcomponents", 1, 0, "JCard");
Expected Behaviour:
When I changed the text in textFieldNachname, it is given to the JCard-Object (this works -- in Debugger I see the internal data of JCard is OK!) and a notification is given to A::slotTest() (this does not work).Test-Case 1:
I can trigger the A::testfct() with the testButton in QML. A::testfct() gives a debug-output which shows, that the internal data is OK. But even the manual signal triggering in A::testfct() here has no effect.Test-Case 2:
Now I have changed A::testfct():void A::testfct() { bool success = connect(_card, SIGNAL(nachnameChanged()), SLOT(slotTest()) ); Q_ASSERT(success); qDebug() << "testfct: " << _card->get_nachname(); _card->nachnameChanged(); }
As you can see I am doing the connection for the signal again. After that, the slotTest() gets notified correctly when I change the content of the textFieldNachname.
So now the question: Why does it not work to do the connection in the constructor? Do you have an idea? Should I make a ticket of this instance?
Best Regards,
Tobias -
Hi Raven,
situation gets more complicated... I try it here:In QML we have a view:
import QtQuick 2.7 import com.mycompany.qmlcomponents 1.0 Page{ A { id: ctrl } textFieldNachname { onTextChanged: ctrl.card.nachname = text; } testButton { onClicked: ctrl.testfct(); } }
Class A we have here:
class A: public QObject { Q_OBJECT Q_PROPERTY(JCard* card READ card CONSTANT) private: JCard *_card; public: JCard *card() const; Q_INVOKABLE void testfct(); private slots: void slotTest(); } A::A(QObject *parent) : QObject(parent) { _card = new JCard(this); bool success; success = connect(_card, SIGNAL(nachnameChanged()), SLOT(slotTest()) ); Q_ASSERT(success); } void A::testfct() { qDebug() << "testfct: " << _card->get_nachname(); _card->nachnameChanged(); } void A::slotTest() { qDebug() << "slotTest: Nachname changed to " << _card->get_nachname(); }
And JCard:
class JCard : public QObject { Q_OBJECT // Creates a property with setters and getters and notification QML_WRITABLE_AUTO_PROPERTY(QString, nachname) public: JCard(QObject *parent = Q_NULLPTR); };
Class A and JCard are given to QML:
qmlRegisterType<A> ("com.mycompany.qmlcomponents", 1, 0, "A"); qmlRegisterType<JCard>("com.mycompany.qmlcomponents", 1, 0, "JCard");
Expected Behaviour:
When I changed the text in textFieldNachname, it is given to the JCard-Object (this works -- in Debugger I see the internal data of JCard is OK!) and a notification is given to A::slotTest() (this does not work).Test-Case 1:
I can trigger the A::testfct() with the testButton in QML. A::testfct() gives a debug-output which shows, that the internal data is OK. But even the manual signal triggering in A::testfct() here has no effect.Test-Case 2:
Now I have changed A::testfct():void A::testfct() { bool success = connect(_card, SIGNAL(nachnameChanged()), SLOT(slotTest()) ); Q_ASSERT(success); qDebug() << "testfct: " << _card->get_nachname(); _card->nachnameChanged(); }
As you can see I am doing the connection for the signal again. After that, the slotTest() gets notified correctly when I change the content of the textFieldNachname.
So now the question: Why does it not work to do the connection in the constructor? Do you have an idea? Should I make a ticket of this instance?
Best Regards,
Tobias@topse
Are there any warnings in the console?Does this output on the console?
textFieldNachname { onTextChanged: { console.log("textChanged:" + text ); ctrl.card.nachname = text; } }
Alternatively you can use QML property binding directly:
Page{ textFieldNachname { id: nachnameInput onTextChanged: ctrl.card.nachname = text; } A { id: ctrl Component.onCompleted: { source = Qt.binding(function() { card.nachname = nachnameInput.text } ); } }
-
@topse
Are there any warnings in the console?Does this output on the console?
textFieldNachname { onTextChanged: { console.log("textChanged:" + text ); ctrl.card.nachname = text; } }
Alternatively you can use QML property binding directly:
Page{ textFieldNachname { id: nachnameInput onTextChanged: ctrl.card.nachname = text; } A { id: ctrl Component.onCompleted: { source = Qt.binding(function() { card.nachname = nachnameInput.text } ); } }
@raven-worx
There are no warnings and it gives output. Thanks for the advice -- seems to be a bit nicer from reading point of view. -
@raven-worx
There are no warnings and it gives output. Thanks for the advice -- seems to be a bit nicer from reading point of view.@topse
then the only thing left i can think of is that the JCard instance you connect in the constructor gets deleted/replaced?
But to make sure, you can move the connect() statement into the constructor ofJCard
, since you also pass an instance ofA
to the constructor. -
@topse
then the only thing left i can think of is that the JCard instance you connect in the constructor gets deleted/replaced?
But to make sure, you can move the connect() statement into the constructor ofJCard
, since you also pass an instance ofA
to the constructor.@raven-worx
:-( What an i***t I am... setting a breakpoint in the destructor... should have done that before... and should not happen... Thanks for giving me a talk :-)Best Regards,
Tobias