2

I'd like to create two checkboxes on different pages of a GUI such that they are semantically the "same" checkbox -- same label, same effect. (Having them on both pages is just for the user's convenience.)

This requires "binding" two CheckBox QML elements together such that the state of one is always reflected by the other, and vice-versa.

This is equivalent to what's being asked here, except I'm using QML/JS instead of JS/JQuery.

I thought that a naive implementation of binding the checked state of each checkbox to some global persistent property would work:

// Global shared state object
pragma Singleton
MySharedState {
    my_feature_on: false
}

Then, on two separate pages, the exact same CheckBox instantiation:

// Checkbox implementation (on both pages
CheckBox {
    checked: MySharedState.my_feature_on
    onClicked: MySharedState.my_feature_on = checked
}

However, this doesn't work, because when a checkbox is clicked, it breaks the initial checked binding. This is the intended behavior, not a bug.

So how can I ensure that two checkboxes always share the same "checked" state?

EDIT: According to a comment bellow, the above implementation will work without modification in Qt Quick Controls 2, which was released with Qt 5.7, so this question only applies to prior versions of Qt (including 5.6, which is a "long-term support" release).

Community
  • 1
  • 1
Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • 1
    For what it's worth, in Qt Quick Controls 2 released in Qt 5.7 it works fine to bind two CheckBoxes to each other, either directly or via such shared state object. – jpnurmi Jun 20 '16 at 09:23
  • @J-PNurmi Awesome. I've edited the question to make a note of that. Thank you. – Kyle Strand Jun 20 '16 at 16:16

2 Answers2

3

When a checkbox is clicked its' checked property is changed and the original checked: MySharedState.my_feature_on binding is removed. You need to create a property binding from Javascript to restore the original binding as explained by J-P Nurmi in the bug report you linked.

For that you have to use Qt.binding().

CheckBox {
    checked: MySharedState.my_feature_on
    onClicked: { // the checked binding is removed since checked has been changed externally to the binding
        MySharedState.my_feature_on = checked
        checked = Qt.binding(function() {return MySharedState.my_feature_on}); //we restore the checked binding
    }
}
GrecKo
  • 6,615
  • 19
  • 23
  • Awesome. I'd been wondering if bindings could be explicitly assigned in a JS context but hadn't looked into it--that seems like a pretty useful trick even beyond this particular instance. – Kyle Strand Jun 17 '16 at 19:33
2

Using a two-way binding with the Binding type works:

import QtQuick 2.5
import QtQuick.Controls 1.0

ApplicationWindow {
    objectName: "window"
    width: 600
    height: 200
    visible: true

    Row {
        CheckBox {
            id: checkBox1
            text: "Check Box 1"
        }

        CheckBox {
            id: checkBox2
            text: "Check Box 2"
        }
    }

    Binding {
        target: checkBox2
        property: "checked"
        value: checkBox1.checked
    }

    Binding {
        target: checkBox1
        property: "checked"
        value: checkBox2.checked
    }
}

Although I'm not sure why it doesn't complain about a binding loop.

Mitch
  • 23,716
  • 9
  • 83
  • 122
  • It *is* weird to me that there's no error about a binding loop--maybe it's because the `checked` signal isn't emitted when the `check` property doesn't actually change? The other oddity here is that the explicit `Binding` object isn't disconnected when the checkbox is clicked, the way the standard `checked:` property-assignment is. This solution does seem to work, but I think I will use the other one, because it makes more sense to me and seems more explicit. – Kyle Strand Jun 17 '16 at 19:33