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. How to make updating of two or more bindings atomic
Forum Updated to NodeBB v4.3 + New Features

How to make updating of two or more bindings atomic

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
13 Posts 4 Posters 2.9k 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.
  • DuBuD Offline
    DuBuD Offline
    DuBu
    wrote on last edited by
    #1

    Hi all,

    how can I make two or more bindings update in a atomic manner when their dependent property change? For example I have the following code:

    property int a: foo[i].a
    property int b: foo[i].b
    

    Now if other properties depent on a or b I want them to not update before a AND b are updated due to a change of i.

    Thanks!

    E 1 Reply Last reply
    0
    • DuBuD DuBu

      Hi all,

      how can I make two or more bindings update in a atomic manner when their dependent property change? For example I have the following code:

      property int a: foo[i].a
      property int b: foo[i].b
      

      Now if other properties depent on a or b I want them to not update before a AND b are updated due to a change of i.

      Thanks!

      E Offline
      E Offline
      Eeli K
      wrote on last edited by Eeli K
      #2

      @DuBu I don't see a way to do that without resorting to imperative code. Maybe the easiest solution is to remove a, b etc. bindings and do instead

      signal abChanged(int a, int b) // or property ab
      onIChanged: {
      a = foo[i].a
      b = foo[i].b
      abChanged(a, b) // or ab = ...
      }
      
      DuBuD 1 Reply Last reply
      0
      • E Eeli K

        @DuBu I don't see a way to do that without resorting to imperative code. Maybe the easiest solution is to remove a, b etc. bindings and do instead

        signal abChanged(int a, int b) // or property ab
        onIChanged: {
        a = foo[i].a
        b = foo[i].b
        abChanged(a, b) // or ab = ...
        }
        
        DuBuD Offline
        DuBuD Offline
        DuBu
        wrote on last edited by DuBu
        #3

        @Eeli-K Too bad. I think to use imperative code instead is not always an option. The opposite way is even worse:

        property int i: foo[a][b]
        

        Property "i" depends on a AND b. That way "i" gets calculated twice. (That's just a simple example.)
        A more complex one:

        onAChanged: calculateComplexGraphic(a, b)
        onBChanged: calculateComplexGraphic(a, b)
        

        Now the calculation gets started twice and the first one is done with a possibly wrong value of a or b.
        I think the whole topic is worth a feature request.

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

          Hi,

          How can you guarantee that you'll change these values atomically on your side ?

          In any case, if you are dependant on several values like that, it might make more sense to encapsulate them in a small strucuture and send that over. That will also make things clearer as you would have only one property to update while you can still provide the single values if you need them like for example size with width and hight.

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

          DuBuD 1 Reply Last reply
          2
          • SGaistS SGaist

            Hi,

            How can you guarantee that you'll change these values atomically on your side ?

            In any case, if you are dependant on several values like that, it might make more sense to encapsulate them in a small strucuture and send that over. That will also make things clearer as you would have only one property to update while you can still provide the single values if you need them like for example size with width and hight.

            DuBuD Offline
            DuBuD Offline
            DuBu
            wrote on last edited by
            #5

            @SGaist I can't guarantee the values get changed atomically on my side. At initialization time they are and at runtime they aren't. For example you have a component with min, max, value. At runtime only value is likely to change. But the initialization takes much longer.
            What do you mean with "small structure"? A js object like ab = { a, b }? That would make changing only one property harder. Or, as you proposed, having both options make everything more complex. Or did I get you wrong?

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

              Why would it be harder ? You are just sending two values, one of which may or may not have changed.

              Can you give a more precise description of your system ? Because currently it seems that you want to synchronise something that is not synchronised at all from the beginning.

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

              DuBuD 1 Reply Last reply
              1
              • SGaistS SGaist

                Why would it be harder ? You are just sending two values, one of which may or may not have changed.

                Can you give a more precise description of your system ? Because currently it seems that you want to synchronise something that is not synchronised at all from the beginning.

                DuBuD Offline
                DuBuD Offline
                DuBu
                wrote on last edited by
                #7

                @SGaist Yes, the values are not synchronized. But that's what I want to achieve, when a value "X" depends on two or more other unsynchronized values how can I avoid to recalculate value "X" each time a value changes. I'm looking for a mechanism to kind of "synchronize" them (Like with imperative code @Eeli-K already proposed.) or as a start a way of ordering their updates (I.e. with foo[a][b] a new "b" doesn't really makes sense with an old "a").

                J.HilkJ E 2 Replies Last reply
                0
                • DuBuD DuBu

                  @SGaist Yes, the values are not synchronized. But that's what I want to achieve, when a value "X" depends on two or more other unsynchronized values how can I avoid to recalculate value "X" each time a value changes. I'm looking for a mechanism to kind of "synchronize" them (Like with imperative code @Eeli-K already proposed.) or as a start a way of ordering their updates (I.e. with foo[a][b] a new "b" doesn't really makes sense with an old "a").

                  J.HilkJ Offline
                  J.HilkJ Offline
                  J.Hilk
                  Moderators
                  wrote on last edited by
                  #8

                  @DuBu

                  To me this seems like the right time to include a cpp-extension to your QML-Project

                  #ifndef ATOMICPROPERTY_H
                  #define ATOMICPROPERTY_H
                  
                  #include <QObject>
                  
                  class atomicProperty :public QObject
                  {
                      Q_OBJECT
                      Q_PROPERTY(int A READ getA WRITE setA NOTIFY AChanged)
                      Q_PROPERTY(int B READ getB WRITE setB NOTIFY BChanged)
                      Q_PROPERTY(int D READ getD NOTIFY DChanged)
                  
                  public:
                      atomicProperty(QObject * parent = Q_NULLPTR): QObject(parent){
                          QObject::connect(this, &atomicProperty::AChanged, this, &atomicProperty::checkD);
                          QObject::connect(this, &atomicProperty::BChanged, this, &atomicProperty::checkD);
                      }
                  
                  public slots:
                      Q_INVOKABLE int getA(){return A;}
                      Q_INVOKABLE int getB(){return B;}
                      Q_INVOKABLE int getD(){return D;}
                  
                      Q_INVOKABLE void setA(int value){
                          if(value != A){
                              A = value;
                              m_aChanged = true;
                              emit AChanged();
                          }
                      }
                  
                      Q_INVOKABLE void setB(int value){
                          if(value != B){
                              B = value;
                              m_bChanged = true;
                              emit BChanged();
                          }
                      }
                  
                  signals:
                      void AChanged();
                      void BChanged();
                      void DChanged();
                  
                  private slots:
                      void checkD(){
                          if(m_aChanged && m_bChanged){
                              m_aChanged = false; m_bChanged = false;
                              D = A+B;
                              emit DChanged();
                          }
                      }
                  
                  private:
                      int A = 0;
                      int B = 0;
                      int D = 0;
                  
                      bool m_aChanged = false;
                      bool m_bChanged = false;
                  };
                  #endif // ATOMICPROPERTY_H
                  
                  

                  expose it to your QML object:

                  qmlRegisterTyp<"atomicProperty",1,0, "atomicProperty");
                  

                  and import it.

                  Than you have the binding you want.


                  Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                  Q: What's that?
                  A: It's blue light.
                  Q: What does it do?
                  A: It turns blue.

                  1 Reply Last reply
                  0
                  • DuBuD DuBu

                    @SGaist Yes, the values are not synchronized. But that's what I want to achieve, when a value "X" depends on two or more other unsynchronized values how can I avoid to recalculate value "X" each time a value changes. I'm looking for a mechanism to kind of "synchronize" them (Like with imperative code @Eeli-K already proposed.) or as a start a way of ordering their updates (I.e. with foo[a][b] a new "b" doesn't really makes sense with an old "a").

                    E Offline
                    E Offline
                    Eeli K
                    wrote on last edited by
                    #9

                    @DuBu The declarative nature of QML and the engine implementation means that you can't rely on the order of execution of certain things. It's just impossible to do what you want and at the same time avoid imperative code. You have to do it either in javascript or in C++. Of course you can encapsulate and hide it somewhere so that you don't need to see it all the time, but the code must be somewhere. I think it would be impossible to change the QML language and engine to do that; it wouldn't be simple and efficient anymore.

                    You have to know two things: 1: The triggering event which triggers property updates and 2: The end of the property updates. In declarative QML code you can know only number 1 but even that without knowing whether other updates will be done or have already been done.

                    The big question in my opinion is "how do you know that all of the properties which are about to change have changed?" Even if you do know the order they are changed the xChanged signal of a property (in pure QML) is sent only if the value has actually changed. So, what happens if A is changed but B is not? Or A is not changed but B is? It's impossible to know without sending xChanged signals even when the properties haven't changed. And if you do that you still have to keep some kind of counter to see when all of the dependencies have changed. You just have to use imperative code.

                    Just give up the idea of a neat declarative QML-only solution and decide how and where you put the imperative code.

                    1 Reply Last reply
                    0
                    • DuBuD Offline
                      DuBuD Offline
                      DuBu
                      wrote on last edited by
                      #10

                      Ok, thanks guys, as you showed there's no implicit way of doing what I want yet. I'll do it explicit (i.e. with imperative code) or try to avoid such scenarios at all.
                      BTW: I wrote a post about an issue in ListView which may be caused by the order of updates:
                      When you want to have 4 items of an ListView filling the whole height of the view you could write the height of a delegate item like:

                      height: ListView.view.height / 4
                      

                      Now if you put 10000 items in the ListView it creates/binds/draws all 10000 in order to remove 9996 again when the height of the ListView gets initialized. If the height of the ListView would be initialized before the delegates, only 4 elements would be created.

                      I would vote for a syntax enhancement to mark a binding as to be updated before other bindings.

                      E 1 Reply Last reply
                      0
                      • DuBuD DuBu

                        Ok, thanks guys, as you showed there's no implicit way of doing what I want yet. I'll do it explicit (i.e. with imperative code) or try to avoid such scenarios at all.
                        BTW: I wrote a post about an issue in ListView which may be caused by the order of updates:
                        When you want to have 4 items of an ListView filling the whole height of the view you could write the height of a delegate item like:

                        height: ListView.view.height / 4
                        

                        Now if you put 10000 items in the ListView it creates/binds/draws all 10000 in order to remove 9996 again when the height of the ListView gets initialized. If the height of the ListView would be initialized before the delegates, only 4 elements would be created.

                        I would vote for a syntax enhancement to mark a binding as to be updated before other bindings.

                        E Offline
                        E Offline
                        Eeli K
                        wrote on last edited by
                        #11

                        @DuBu Delegates are created on-the-fly, there will be maybe six extra objects beyond the visible ones.

                        DuBuD 1 Reply Last reply
                        0
                        • E Eeli K

                          @DuBu Delegates are created on-the-fly, there will be maybe six extra objects beyond the visible ones.

                          DuBuD Offline
                          DuBuD Offline
                          DuBu
                          wrote on last edited by
                          #12

                          @Eeli-K Yes, but it seems if the height of the delegate is 0, then all items are visible and therefore created. I workaround that by writing:

                          height: (ListView.view.height || 1) / 4
                          
                          1 Reply Last reply
                          0
                          • DuBuD Offline
                            DuBuD Offline
                            DuBu
                            wrote on last edited by
                            #13

                            There's another issue we noticed related to that topic:
                            We have a couple of nested Grid-/Column-/RowLayouts together with Items which have margins. At the end of that hierarchy there's a Canvas components which draws arcs. Sometimes (!) during startup/inititalization and between two updates of bindings one of the layout components has a width of 0. If that happens the arc function of the canvas complains about an invalid radius being negative, which results from the 0 width and the margins. After initialization everything works fine and there's no item with a width of 0 anymore. It's just a matter of the order the layouts calculate it's sizes. It also shows that the arcs get redrawn a couple of times during initialization.

                            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