Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-71577

QML needs a way to atomically update multiple bindings at the same time

    XMLWordPrintable

Details

    Description

      When writing QML code you frequently need to update multiple properties of an object at the same time. For example, you may want to reposition an Item, setting width, height, x, and y. There are typically bindings that react to several of those properties. That could be a binding that calculates a color for a rectangle based on its size will bind to width and height. If you update width and height in sequence, the binding will be executed twice, with one of the executions being useless and potentially harmful. Having an intermediate color that gets discarded is "only" wasteful, but you may have a binding or signal handler in place that monitors the rectangle's size and pops up a message if it gets too small. Then, the intermediate size actually leads to malfunctioning of the program.

      Such things frequently happen in practice, especially if you write complex interdependent bindings, as is encouraged by QML. The resulting problems are very hard to track down. Once you've found them, you can guard against them by adding "update block" properties that tell the downstream bindings and signal handlers "hold on, there is more". However, the resulting code gets extremely ugly. We should provide language level support for setting multiple properties atomically.

      Considering the way QML is integrated with the Qt property system, there is no simple solution to this problem. As long as width and height are separate properties, the C++ implementation of Item will send two signals when you update them both. One way to address this would be connecting the signals in a "queued unique" way. That is the execution of the binding is deferred to the next event loop iteration, and multiple triggers to the same binding are collapsed into one. The "delayed" property on Binding does something similar, but it still causes the binding to be evaluated multiple times if multiple signals have arrived. It even recreates the intermediate values for properties that have since been changed. Consider, for example:

      import QtQuick 2.9
      import QtQuick.Window 2.2
      
      Window {
          id: w
          visible: true
          width: 640
          height: 480
      
          property int area
      
          Binding {
              target: w
              property: "area"
              delayed: true
              value: width * height
          }
      
          onAreaChanged: console.log(area)
      
          Timer {
              onTriggered: {
                  console.log("triggered");
                  width += 10
                  height += 10
                  console.log("done", width * height);
              }
      
              interval: 1000
              running: true
              repeat: true
          }
      }
      

      This outputs:

      qml: 307200
      qml: triggered
      qml: done 318500
      qml: 312000
      qml: 318500
      qml: triggered
      qml: done 330000
      qml: 323400
      qml: 330000
      qml: triggered
      [...]
      

       

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              qt.team.quick.subscriptions Qt Quick and Widgets Team
              ulherman Ulf Hermann
              Votes:
              7 Vote for this issue
              Watchers:
              18 Start watching this issue

              Dates

                Created:
                Updated:

                Gerrit Reviews

                  There are no open Gerrit changes