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