Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Binding loop problem in an application with QML&C++

Binding loop problem in an application with QML&C++

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
6 Posts 3 Posters 1.0k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Q Offline
    Q Offline
    QTLeearn
    wrote on last edited by
    #1

    Hi, I am doing a project, basically what I want to do is to synchronize a property between 2 boxes in 2 QML files, I bind that property to a C++ Q_PROPERTY, so by binding the 2 boxes to the same C++ Q_PROPERTY, the synchronization can be achieved.
    Here is my code in Box A

                    Box_A {
                        id: box_A
                        // sth
     Binding { target:box_A; property: "theProperty"; value:model.CppModel.theProperty } 
                        onThePropertyChanged: {
                            model.CppModel.theProperty = theProperty
                        }
                    }
    

    In Box_B

                    Box_B {
                        id: box_B
                        // sth
     Binding { target:box_B; property: "theProperty"; value:model.CppModel.theProperty } 
                        onThePropertyChanged: {
                            model.CppModel.theProperty = theProperty
                        }
                    }
    

    The problem is that once I change theProperty in either box A or B, the qt creator complains that loop binding detected for value: model.CppModel.theProperty, is there a way of walking around this problem? Thank you very much

    1 Reply Last reply
    0
    • fcarneyF Offline
      fcarneyF Offline
      fcarney
      wrote on last edited by
      #2

      Does using Connections help?:

      import QtQuick 2.9
      import QtQuick.Window 2.2
      import QtQuick.Controls 2.5
      
      Window {
          visible: true
          width: 640
          height: 480
          title: qsTr("Sync Objects Testing")
      
          // can be C++ object in Connections source property
          // rectangle used as stand in
          Rectangle {
              id: source
      
              border.color: "red"
              border.width: 2
              width: 100
              height: 100
      
              property int theproperty: 0
      
              onThepropertyChanged: {
                  console.log("source changed")
              }
      
              Text {
                  anchors.horizontalCenter: parent.horizontalCenter
                  anchors.verticalCenter: parent.verticalCenter
                  text: parent.theproperty
              }
          }
          Rectangle {
              id: boxa
      
              anchors.top: source.bottom
              border.color: "green"
              border.width: 2
              width: 100
              height: 100
      
              property int theproperty: 0
      
              Connections {
                  target: source
                  onThepropertyChanged: {
                      boxa.theproperty = target.theproperty
                  }
              }
      
              onThepropertyChanged: {
                  console.log("boxa changed")
                  source.theproperty = theproperty
              }
      
              Text {
                  anchors.horizontalCenter: parent.horizontalCenter
                  anchors.verticalCenter: parent.verticalCenter
                  text: parent.theproperty
              }
          }
          Rectangle {
              id: boxb
      
              anchors.top: boxa.bottom
              border.color: "blue"
              border.width: 2
              width: 100
              height: 100
      
              property int theproperty: 0
      
              Connections {
                  target: source
                  onThepropertyChanged: {
                      boxb.theproperty = target.theproperty
                  }
              }
      
              onThepropertyChanged: {
                  console.log("boxb changed")
                  source.theproperty = theproperty
              }
      
              Text {
                  anchors.horizontalCenter: parent.horizontalCenter
                  anchors.verticalCenter: parent.verticalCenter
                  text: parent.theproperty
              }
          }
      
          Button {
              id: but_change
      
              anchors.top: boxb.bottom
              text: "change"
      
              onClicked: {
                  var num = Math.round(Math.random() * 100)
                  console.log(num)
                  source.theproperty = num
              }
          }
          Button {
              id: but_boxa
      
              anchors.top: but_change.bottom
              text: "boxa change"
      
              onClicked: {
                  var num = Math.round(Math.random() * 100)
                  console.log(num)
                  boxa.theproperty = num
              }
          }
          Button {
              id: but_boxb
      
              anchors.top: but_boxa.bottom
              text: "boxb change"
      
              onClicked: {
                  var num = Math.round(Math.random() * 100)
                  console.log(num)
                  boxb.theproperty = num
              }
          }
      
      }
      

      I wanted to understand this a bit better myself. I am curious what happens when Connections is connected to an actual C++ signal. I could see the same loop happening if the update of the source blindly emits that signal.

      C++ is a perfectly valid school of magic.

      1 Reply Last reply
      0
      • fcarneyF Offline
        fcarneyF Offline
        fcarney
        wrote on last edited by
        #3

        I tested this with an actual C++ object.
        main.cpp:

        #include <QGuiApplication>
        #include <QQmlApplicationEngine>
        #include <QQmlContext>
        
        class IntSource : public QObject
        {
            Q_OBJECT
        
        private:
            int value;
        public:
            Q_PROPERTY(int theproperty READ getValue WRITE setValue NOTIFY thepropertyChanged)
        
            int getValue(){return value;};
            void setValue(int invalue){
                value = invalue;
                emit thepropertyChanged(value);
            }
        
        signals:
            void thepropertyChanged(int thevalue);
        };
        
        int main(int argc, char *argv[])
        {
            QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        
            QGuiApplication app(argc, argv);
        
            QQmlApplicationEngine engine;
        
            IntSource intsource;
            engine.rootContext()->setContextProperty("intsource", &intsource);
        
            engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
            if (engine.rootObjects().isEmpty())
                return -1;
        
            return app.exec();
        }
        
        #include "main.moc"
        

        main.qml:

        import QtQuick 2.9
        import QtQuick.Window 2.2
        import QtQuick.Controls 2.5
        
        Window {
            visible: true
            width: 640
            height: 540
            title: qsTr("Sync Objects Testing")
        
            Component.onCompleted: {
                intsource.theproperty = 0
            }
        
            // can be C++ object in Connections source property
            // rectangle used as stand in
            Rectangle {
                id: source
        
                border.color: "red"
                border.width: 2
                width: 100
                height: 100
        
                property int theproperty: 0
        
                onThepropertyChanged: {
                    console.log("source changed")
                }
        
                Text {
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.verticalCenter: parent.verticalCenter
                    text: parent.theproperty
                }
            }
            Rectangle {
                id: source2
        
                anchors.top: source.bottom
                border.color: "red"
                border.width: 2
                width: 100
                height: 100
        
                property int theproperty: 0
        
                Connections {
                    target: intsource
                    onThepropertyChanged: {
                        console.log("intsource changed")
                        source2.theproperty = target.theproperty
                    }
                }
        
                Text {
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.verticalCenter: parent.verticalCenter
                    text: parent.theproperty
                }
            }
            Rectangle {
                id: boxa
        
                anchors.top: source2.bottom
                border.color: "green"
                border.width: 2
                width: 100
                height: 100
        
                property int theproperty: 0
        
                Connections {
                    target: intsource
                    onThepropertyChanged: {
                        boxa.theproperty = target.theproperty
                    }
                }
        
                onThepropertyChanged: {
                    console.log("boxa changed")
                    intsource.theproperty = theproperty
                }
        
                Text {
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.verticalCenter: parent.verticalCenter
                    text: parent.theproperty
                }
            }
            Rectangle {
                id: boxb
        
                anchors.top: boxa.bottom
                border.color: "blue"
                border.width: 2
                width: 100
                height: 100
        
                property int theproperty: 0
        
                Connections {
                    target: intsource
                    onThepropertyChanged: {
                        boxb.theproperty = target.theproperty
                    }
                }
        
                onThepropertyChanged: {
                    console.log("boxb changed")
                    intsource.theproperty = theproperty
                }
        
                Text {
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.verticalCenter: parent.verticalCenter
                    text: parent.theproperty
                }
            }
        
            Button {
                id: but_change
        
                anchors.top: boxb.bottom
                text: "change"
        
                onClicked: {
                    var num = Math.round(Math.random() * 100)
                    console.log(num)
                    intsource.theproperty = num
                }
            }
            Button {
                id: but_boxa
        
                anchors.top: but_change.bottom
                text: "boxa change"
        
                onClicked: {
                    var num = Math.round(Math.random() * 100)
                    console.log(num)
                    boxa.theproperty = num
                }
            }
            Button {
                id: but_boxb
        
                anchors.top: but_boxa.bottom
                text: "boxb change"
        
                onClicked: {
                    var num = Math.round(Math.random() * 100)
                    console.log(num)
                    boxb.theproperty = num
                }
            }
        
        }
        

        Why doesn't this form an endless loop? While the bind method does?

        C++ is a perfectly valid school of magic.

        1 Reply Last reply
        1
        • fcarneyF Offline
          fcarneyF Offline
          fcarney
          wrote on last edited by fcarney
          #4

          Okay, I get it. The onThepropertyChanged callbacks actually look to see if the property changed. If it didn't then it doesn't register a change and does not fire a signal. The C++ object however has no such checks and probably should. If 2 C++ objects as written were bound to each other like this they would indeed keep firing as there are no checks.

          The question is then: why the Binding would cause a loop? The property should have the same protections of only emitting a change when the value actually changes. Perhaps Binding is blind and needs the "when" defined:

          Binding {
            target: box_A 
            property: "theProperty"
            value: model.CppModel.theProperty
            when: theProperty !== model.CppModel.theProperty
          }
          

          Edit: I mean, it works, but prints out a binding loop message for some reason. The value does get updated properly.

          C++ is a perfectly valid school of magic.

          Q 1 Reply Last reply
          2
          • fcarneyF fcarney

            Okay, I get it. The onThepropertyChanged callbacks actually look to see if the property changed. If it didn't then it doesn't register a change and does not fire a signal. The C++ object however has no such checks and probably should. If 2 C++ objects as written were bound to each other like this they would indeed keep firing as there are no checks.

            The question is then: why the Binding would cause a loop? The property should have the same protections of only emitting a change when the value actually changes. Perhaps Binding is blind and needs the "when" defined:

            Binding {
              target: box_A 
              property: "theProperty"
              value: model.CppModel.theProperty
              when: theProperty !== model.CppModel.theProperty
            }
            

            Edit: I mean, it works, but prints out a binding loop message for some reason. The value does get updated properly.

            Q Offline
            Q Offline
            QTLeearn
            wrote on last edited by
            #5

            @fcarney Thank you for your help, I can take a good look at your answer

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              @fcarney said in Binding loop problem in an application with QML&C++:

              If 2 C++ objects as written were bound to each other like this they would indeed keep firing as there are no checks.

              Indeed they will and it even has a name: a signal storm :)

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved