Details
-
Task
-
Resolution: Unresolved
-
P2: Important
-
None
-
None
-
None
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
- relates to
-
QTBUG-73675 Streamline QML for better toolability and performance
- Open
-
QTBUG-75672 New property system
- Closed
-
QTBUG-76016 The QML language has some systematic problems
- Reported
-
QTBUG-11712 all bindings should be updated before any onXXXChanged handler run
- Open
-
QTBUG-22400 Need a way to avoid redundant function calls
- Closed