Qt 5.4/Qml: Prevent binding loop

前端 未结 5 1319
孤街浪徒
孤街浪徒 2021-02-19 04:04

I have a global singleton \"Settings\" which holds application settings. When I try to run the following code I get a QML CheckBox: Binding loop detected for property \"ch

5条回答
  •  后悔当初
    2021-02-19 04:49

    I prefer this solution

    // Within the model
    Q_PROPERTY(bool someSetting READ getSomeSetting WRITE setSomeSetting NOTIFY someSettingChanged)
    
    void SettingsModel::setSomeSetting(bool checkValue) {
        if (m_checkValue != checkValue) {
            m_checkValue = checkValue;
            emit someSettingChanged();
        }
    }
    
    // QML
    CheckBox {
        checked: Settings.someSetting                         
        onCheckedChanged: Settings.someSetting = checked
    }
    

    The trick is you protect the emit with an if check in the model. This means you still get a binding loop but only a single one, not an infinite one. It stops when that if check returns false thereby not emitting to continue the loop. This solution is very clean, you do not get the warning, and yet you still get all the benefits of the binding.

    I want to talk about the limitations of the other solutions presented

    CheckBox {    
        Component.onCompleted: checked = Settings.someSetting
        onCheckedChanged: Settings.someSetting = checked; 
    }
    

    In this solution you lose your binding. It can only have a default setting on creation and be changed by the user. If you expand your program such that other things change the values in your model, this particular view will not have a way to reflect those changes.

    Settings {
        id: mySettings
        onSomeSettingChanged: checkBox.checked = someSetting
    }
    CheckBox {
        id: checkBox
        onCheckedChanged: mySettings.someSetting = checked
    }
    

    This solution was mentioned to address these problems but never written out. It is functionally complete. Model changes are reflected, the user can change the data, and there are no binding loops because there are no bindings; only two discrete assignments. (x: y is a binding, x = y is an assignment)

    There are a couple problems with this. The first is that I think its ugly and inelegant, but that is arguably subjective. It seems fine here but if you have a model representing 10 things in this view, this turns into signal spaghetti. The bigger problem is that it does not work well with delegates because they only exist on demand.

    Example:

    MyModel {
        id: myModel
    
        // How are you going to set the check box of a specific delegate when
        // the model is changed from here?
    }
    ListView {
        id: listView
        model: myModel.namesAndChecks
        delegate: CheckDelegate {
            id: checkDelegate
            text: modelData.name
            onCheckStateChanged: modelData.checkStatus = checked
        }
    }
    

    You can actually do it. I've made up custom QML signals and connections to do it, but the code complexity makes me want to hurl, and even worse you could possibly be forcing creation of a delegate when it is not necessary.

提交回复
热议问题