Qt 5.4 / Qml:防止绑定循环

时间:2015-01-31 10:44:49

标签: c++ qt qml qt5 qt5.4

我有一个全球单身人士"设置"它保存应用程序设置。当我尝试运行以下代码时,我得到QML CheckBox: Binding loop detected for property "checked"

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

很明显为什么会出现这种错误,但是如何在没有绑定循环的情况下正确实现此功能呢?例如。我想在设置单例中保存当前选中状态的复选框。

我正在使用Qt 5.4和Qml Quick 2。

此致

5 个答案:

答案 0 :(得分:13)

不要绑定它。因为复选框不完全依赖Setting.someSetting

当用户点击该复选框时,CheckBox.checked会自行更改。同时,属性绑定不再有效。 Settings.someSetting在用户点击后无法修改CheckBox。因此,checked: Settings.someSetting绑定是错误的。

如果要在组件准备就绪时为复选框分配初始值,请使用Component.onCompleted进行分配:

CheckBox {
    id: someSettingCheckBox 

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

如果您正在处理更复杂的场景,Setting.someSetting可能会在运行时被其他一些内容更改,并且复选框的状态需要同时更改。捕获onSomeSettingChanged信号并明确更改复选框。仅在程序/小部件/对话框/ xxx完成时才将someSettingCheckBox的值提交到Settings

CheckBox { id: someSettingCheckBox }

//within the Settings, or Connection, or somewhere that can get the signal.
onSomeSettingChanged: someSettingCheckBox.checked = someSetting

答案 1 :(得分:4)

如果您不想进行绑定循环 - 请勿进行绑定,例如使用代理变量。其他简单的解决方案可以是检查值:

CheckBox {
    checked: Settings.someSetting                         
    onCheckedChanged: {
        if (checked !== Settings.someSetting) {
            Settings.someSetting = checked;
        }
    }
}

答案 2 :(得分:4)

您还可以进行双向绑定以解决此问题:

28

答案 3 :(得分:4)

我更喜欢这种解决方案

// 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
}

诀窍是您可以通过在模型中进行if检查来保护emit。这意味着您仍然会得到一个绑定循环,但只有一个,而不是无限循环。当检查返回false时,它停止,从而不发出继续循环。此解决方案非常干净,您没有收到警告,但仍然可以获得绑定的所有好处。

我想谈谈提出的其他解决方案的局限性

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

在此解决方案中,您会失去绑定。它只能在创建时具有默认设置,并且可以由用户更改。如果您扩展程序以使其他事情更改模型中的值,则该特定视图将无法反映这些更改。

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

提到此解决方案是为了解决这些问题,但从未写出。功能齐全。反映了模型更改,用户可以更改数据,并且因为没有绑定,所以没有绑定循环;只有两个离散的任务。 (x: y是绑定,x = y是分配)

这有几个问题。首先,我认为它丑陋且不雅,但这可以说是主观的。在这里看起来不错,但是如果您有一个在这个视图中代表10个事物的模型,那么这将变成意大利面条。更大的问题是,由于委托仅按需存在,因此不适用于委托。

示例:

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
    }
}

您实际上可以做到。我已经完成了自定义QML信号和连接的创建,但是代码的复杂性使我想要抛出错误,甚至更糟的是,您可能在不必要时强制创建委托。

答案 4 :(得分:1)

有时将控制中的输入和输出值分开是有用的。在这种情况下,控件始终显示实际值,并且还可以向用户显示延迟。

CheckBox {
    checked: Settings.someSetting
    onClicked: Settings.someSetting = !checked
}