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


  • Moderators

    @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...



  • @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.


  • Moderators

    @Tikani
    well the most effortless way is to use QML's property binding feature


  • Moderators

    @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...



  • @raven-worx

    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


  • Moderators

    @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.


  • Moderators

    @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 of JCard, since you also pass an instance of A 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


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.