Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Why is this a binding loop?



  • Hi all -

    I have a class with the following in it:

    Q_PROPERTY( QStringList posterModalChecklist
    	MEMBER m_posterModalChecklist
    	READ GetPosterModalChecklist
            NOTIFY EventPosterModalChecklistChangedForQML )
    			
    QStringList m_posterModalChecklist;
    void EventPosterModalChecklistChangedForQML(QStringList qsl);
      
    QStringList ChangeConsumables::GetPosterModalChecklist() {
      m_posterModalChecklist = m_batch[m_index]->PosterModalChecklist();
      emit EventPosterModalChecklistChangedForQML(m_posterModalChecklist);
      return m_posterModalChecklist;
    }
    

    Used in this QML:

    Column {
      Repeater {
    	model: changeConsumablesViewModel.posterModalChecklist
    	RowLayout {
    	  Label {
    		text: modelData
    

    At runtime I get an error about a binding loop on "model." The error was introduced when I put in the emit() call. What might I be doing wrong?

    Thanks...



  • Hi, @mzimmers. Mmmm, question... why do you use a signal emission on a getter? I think it is unnecessary. I mean, Why do you want to inform to qml side of a change, if there is not? Unless your PosterModalChecklist() function modify your model... Is this your case?



  • @mzimmers said in Why is this a binding loop?:

    At runtime I get an error about a binding loop on "model." The error was introduced when I put in the emit() call. What might I be doing wrong?

    First you should understand how binding loops are detected, therefore take a look at this video from KDAB: Introduction to Qt / QML (Part 11) - Binding Loops.

    Now to solve your issue: remove the emit in the getter. You should only emit a notification on property value change, the purpose of this is to enable QML to be aware about changes. So property changes on C++ side can automatically trigger updates on QML side.
    If you trigger notification in C++ side with the getter, then:

    1. QML is reading property with getter
    2. QML is notified about property value change
    3. QML reread property value with getter
    4. QML is notified about property value change
    5. QML detect binding loop!


  • Thank you both for the help. To answer your question about why there was a signal on a get -- the way this app was written, the get not only returned the current value, but also updated it first (I know it's weird).

    To fix this, I've created a setter for the property:

    QStringList ChangeConsumables::GetPosterModalChecklist() {
      return m_posterModalChecklist;
    }
    
    void ChangeConsumables::SetPosterModalChecklist(QStringList qsl) {
      m_posterModalChecklist = qsl;
      emit EventPosterModalChecklistChangedForQML(m_posterModalChecklist);
    }
    

    The code is a little convoluted, as the setter is actually invoked by another object (the same one that's providing the QStringList to the getter), but it seems to work.

    Thanks again...


  • Lifetime Qt Champion

    Hi,

    There's one thing missing from your setter: the equality guard. Usually the first thing done in a setter is check if the value changes and stop there if not. This allows to avoid signal storms were each party connected re-emits the same values over and over.



  • @SGaist so, like this?

    void ChangeConsumables::SetPosterModalChecklist(QStringList qsl) {
      if (m_posterModalChecklist != qsl) {
          m_posterModalChecklist = qsl;
          emit EventPosterModalChecklistChangedForQML(m_posterModalChecklist);
      }
    }
    

  • Lifetime Qt Champion

    Either that or

    void ChangeConsumables::SetPosterModalChecklist(QStringList qsl) {
      if (m_posterModalChecklist == qsl) {
          return;
    }
      m_posterModalChecklist = qsl;
      emit EventPosterModalChecklistChangedForQML(m_posterModalChecklist);
    }
    

    Depending on your taste.
    Note that I have seen this version more frequently. Also in Qt's own sources.



  • @SGaist I realize I'm old school, but I really don't like return statements in the middle of a function. I know it's done by far better coders than I, but I'm just not a fan.


  • Lifetime Qt Champion

    I understand your point.

    IIRC, early returns like these may allow some optimization by the compiler.

    It's technically also at least one less operation because != is usually implemented as ! ==.



  • Not to be argumentative, but I'd venture that a routine that is comparing QStringList objects probably leaves much to be desired in the optimization department anyway.

    Your comment about !== is interesting. Is there a way for the programmer to explicitly effect a similar comparison?


  • Lifetime Qt Champion

    @mzimmers said in Why is this a binding loop?:

    Not to be argumentative, but I'd venture that a routine that is comparing QStringList objects probably leaves much to be desired in the optimization department anyway.

    I was more in the generic case of simple type comparison :-)

    @mzimmers said in Why is this a binding loop?:

    Your comment about !== is interesting. Is there a way for the programmer to explicitly effect a similar comparison?

    I am not sure I am following you on that one.



  • @SGaist said in Why is this a binding loop?:

    I am not sure I am following you on that one.

    Well, of course C++ doesn't have a "!==" operator, so I assumed you meant that it first compares type, then content (a la Javascript). I was just curious how a C++ coder might perform that type check explicitly.


  • Lifetime Qt Champion

    You missed the space between the ! and the == :-)

    I meant it as: "!(a == b)"



  • Oh...I hate it when I do that (heh).

    But now my statement on efficiency is even more true...

    EDIT:

    For those who really wish to exact that last bit of optimization, you could always do something like this:

    void ChangeConsumables::SetPosterModalChecklist(QStringList qsl) {
      do {
        if (m_posterModalChecklist == qsl) {
          continue;
        }
        if (any other reasons to exclude further processing) {
          continue;
        }
        m_posterModalChecklist = qsl;
        emit EventPosterModalChecklistChangedForQML(m_posterModalChecklist);
      } while (false);
    }
    

    This technique is also very handy for avoiding multiple if statements (with their annoying attendant indentation).


Log in to reply