Raymii.org
Quis custodiet ipsos custodes?Home | About | All pages | Cluster Status | RSS Feed
Qt/QML Property bindings break after a JavaScript assignment
Published: 19-05-2022 05:30 | Author: Remy van Elst | Text only version of this article
❗ This post is over two years old. It may no longer be up to date. Opinions may have changed.
Table of Contents
Property bindings are one of the most powerful concepts in Qt/QML. Property bindings let you specify relationships between different object properties. When a properties dependencies change in value, the property is automatically updated according to the specified relationship. The QML engine monitors the properties dependencies (that is, the variables in the binding expression). When a change is detected, the QML engine re-evaluates the binding expression and applies the new result to the property.
One little known caveat with property bindings is that they break after a static JavaScript assignment (property = value
). This post shows you the different behaviors and how to use Qt.binding()
to assign property bindings via JavaScript.
Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:
I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!
Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.
You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!
Do note that this behavior is intended, documented and can be useful in situations where you want to purposely want to break a property binding.
This post is intended to inform you of the different behaviors, as it can be confusing if you're unaware of what is happening. A colleague of mine was working on a bug, he was not aware of this behavior and it cost him two hours to figure out that it was the underlying issue. Could happen to me as well when you're knee-deep into a debugging session.
Demonstration
The image above demonstrates the issue.
The top Button
(id: boundButton
) text
property is bound to the
TextField
(id: bindableTextField
) text
property, so when you edit the
text, the text on the button updates automatically.
The second button breaks the binding by doing a JavaScript assignment in it's
onClicked
function:
onClicked: boundButton.text = "Binding broken"
If you've clicked the button and then change the text in the TextField
,
the button's text no longer reflects the text you've typed.
The last button restores the property binding using the Qt.binding()
function:
onClicked: boundButton.text = Qt.binding(function () {
return bindableTextField.text
})
}
A bit convoluted syntax wise, but it gets the job done.
Qt.binding()
The documentation states the following:
Returns a JavaScript object representing a property binding, with a function that evaluates the binding. There are two main use-cases for the function: firstly, to apply a property binding imperatively from JavaScript code and secondly, to apply a property binding when initializing property values of dynamically constructed objects (via
Component.createObject()
orLoader.setSource()
).
Confusingly, that page has examples for the second use case, not the first. Simpler examples are on the other doc page, transcribed below:
The Rectangle below initially ensures that its height is always twice its
width
. However, when the space key is pressed, the current value of
width*3
will be assigned to height as a static value. After that, the
height will remain fixed at this value, even if the width changes. The
assignment of the static value removes the binding.
Rectangle {
width: 100
height: width * 2
focus: true
Keys.onSpacePressed: {
height = width * 3
}
}
If the intention is to give the rectangle a fixed height and stop automatic
updates, then this is not a problem. However, if the intention is to
establish a new relationship between width and height, then the new binding
expression must be wrapped in the Qt.binding()
function instead:
Rectangle {
width: 100
height: width * 2
focus: true
Keys.onSpacePressed: {
height = Qt.binding(function() { return width * 3 })
}
}
Now, after the space key is pressed, the rectangle's height will continue auto-updating to always be three times its width.
The other use case is, in my experience, not used often, or at least not in the projects I've worked on. Dynamically creating QML objects is fun at first, but you loose type safety and it's all strings, so changing one name will work fine at first, until you get runtime errors somewhere else. Maybe I'm spoiled by the compile-time safety, but such a small typo or rename has caught me more than I'd like to admit. Whereas, in C++ it'll just won't compile anymore.
Tags: articles , bindings , c++ , cpp , javascript , qml , qt , qt5 , qt6