In QML (at least until version 5.61), property bindings are broken once a new value is assigned in a JavaScript context:
Item {
property bool sourceBool: <some changing value e.g. from C++ code>
property bool boundBool: sourceBool
function reassignBool() {
boundBool = true;
}
}
Once reassignBool is called, boundBool will be true regardless of the state of sourceBool.
I would like to be able to assign a temporary value to a property that will not break the original binding; the temporary value would persist until any of the NOTIFY signals associated with the original binding are triggered, at which point the bound property would again reflect the value computed by the binding.
As an example use-case, suppose I have a button that I don't want the user to be able to press twice in a row, and which is enabled or disabled according to some set of rules:
MyButton {
id: onceOnlyButton
// Suppose the QML type `MyButton` provides a boolean property
// called `enabled` that greys out the button and prevents it from
// being clicked when `false`.
enabled: <condition1> && <scondition2>
onClicked: {
<schedule some asynchronous work>
}
}
Suppose that, when the button is clicked, <condition1> will eventually become false, so that the button would eventually be disabled appropriately--but not immediately.
So I would like to do something like the following:
....
onClicked: {
enabled = false
<schedule some asynchronous work>
enabled = Qt.binding(function() {
return <condition1> && <condition2>
}
}
... but of course as soon as I re-establish the binding, since condition1 hasn't yet become false, enabled will become true again.
I suppose I could create some kind of Connections object that would connect the NOTIFY signal(s) associated with <condition1> to a handler that would invoke Qt.binding, then....delete the Connections object, I suppose. But this seems pretty complicated and inelegant.
Is there any way to ensure that enabled is set to false immediately, then re-bound to the NOTIFY signals for <condition1> and <condition2> as soon as any of those signals are emitted?
1 I'm not entirely sure how assignments affect bindings in 5.7, but I know that assigning a value to a property does not automatically break the binding. So this might just work automagically out-of-the-box in 5.7.
This should be fairly straightfoward to achieve using QML Binding type, which is great for temporarily overriding a binding without destroying it (and it has been around since before version 5.6!)
You will need a variable or property which you can use in the when property of the Binding type. In the example below I have used timer.running because the asynchronous work scheduled here is just a timer.
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
property bool condition1: true
property bool condition2: true
Button {
id: onceOnlyButton
enabled: condition1 && condition2
Binding on enabled {
when: timer.running
value: false
}
onClicked: {
timer.start()
}
text: "onceOnlyButton"
}
// stuff below is just for demonstration
Button {
anchors.left: onceOnlyButton.right
text: "toggle Condition1\n(to prove binding still intact)"
onClicked: condition1 = !condition1
}
Timer {
id: timer
interval: 2000
running: false
repeat: false
onTriggered: condition1 = false
}
}
If you don't have a variable you can use to establish whether the asynchronous work is still going on (like my timer.running) then you will need to create one, as below property forceButtonDisable. This makes it look more complicated, so lets wrap it up inside a new re-usable component:
import QtQuick 2.7
import QtQuick.Controls 2.0
Item {
id: control
property alias text: button.text
property bool enabled
property bool forceButtonDisable: false
signal clicked()
onEnabledChanged: forceButtonDisable = false
width: button.implicitWidth
height: button.implicitHeight
Button {
id: button
width: control.width
height: control.height
enabled: control.enabled
Binding on enabled {
when: control.forceButtonDisable
value: false
}
onClicked: {
control.forceButtonDisable = true
control.clicked()
}
}
}
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
property bool condition1: true
property bool condition2: true
// once clicked, the button is temporarily disabled until original binding expression results in a different value
OnceOnlyButton {
id: onceOnlyButton
text: "onceOnlyButton"
enabled: condition1 && condition2
onClicked: timer.start()
}
// stuff below is just for demonstration
Button {
anchors.left: onceOnlyButton.right
text: "toggle Condition1\n(to prove binding still intact)"
onClicked: condition1 = !condition1
}
Timer {
id: timer
interval: 2000
running: false
repeat: false
onTriggered: condition1 = false
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With