Qt 5.4/Qml:防止绑定循环
Qt 5.4/Qml: Prevent binding loop
我有一个保存应用程序设置的全局单例 "Settings"。当我尝试 运行 以下代码时,我得到 QML CheckBox: Binding loop detected for property "checked"
:
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
Settings.someSetting = checked;
}
}
很明显为什么会出现这个错误,但是我如何在没有绑定循环的情况下正确实现这个功能呢?例如。我想在设置单例中保存复选框的当前选中状态。
我正在使用 Qt 5.4 和 Qml Quick 2。
此致,
如果您不想进行绑定循环 - 请不要进行绑定,例如使用代理变量。其他简单的解决方案可以是检查值:
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
if (checked !== Settings.someSetting) {
Settings.someSetting = checked;
}
}
}
不要绑定。因为复选框并不完全依赖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
信号并显式更改复选框。只有在 program/widget/dialog/xxx 完成后才将 someSettingCheckBox
的值提交给 Settings
。
CheckBox { id: someSettingCheckBox }
//within the Settings, or Connection, or somewhere that can get the signal.
onSomeSettingChanged: someSettingCheckBox.checked = someSetting
您也可以通过双向绑定来解决这个问题:
CheckBox {
id: checkBox
Binding { target: checkBox; property: "checked"; value: Settings.someSetting }
Binding { target: Settings; property: "someSetting"; value: checkBox.checked }
}
有时在控件中分离输入值和输出值很有用。在这种情况下,控件始终显示实际值,它也可以向用户显示延迟。
CheckBox {
checked: Settings.someSetting
onClicked: Settings.someSetting = !checked
}
我更喜欢这个解决方案
// 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
。这意味着你仍然得到一个绑定循环,但只有一个,而不是无限循环。当 if check returns 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 信号和连接来执行此操作,但代码的复杂性让我想投掷,更糟糕的是,您可能会在不需要时强制创建委托。
我有一个保存应用程序设置的全局单例 "Settings"。当我尝试 运行 以下代码时,我得到 QML CheckBox: Binding loop detected for property "checked"
:
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
Settings.someSetting = checked;
}
}
很明显为什么会出现这个错误,但是我如何在没有绑定循环的情况下正确实现这个功能呢?例如。我想在设置单例中保存复选框的当前选中状态。
我正在使用 Qt 5.4 和 Qml Quick 2。
此致,
如果您不想进行绑定循环 - 请不要进行绑定,例如使用代理变量。其他简单的解决方案可以是检查值:
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
if (checked !== Settings.someSetting) {
Settings.someSetting = checked;
}
}
}
不要绑定。因为复选框并不完全依赖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
信号并显式更改复选框。只有在 program/widget/dialog/xxx 完成后才将 someSettingCheckBox
的值提交给 Settings
。
CheckBox { id: someSettingCheckBox }
//within the Settings, or Connection, or somewhere that can get the signal.
onSomeSettingChanged: someSettingCheckBox.checked = someSetting
您也可以通过双向绑定来解决这个问题:
CheckBox {
id: checkBox
Binding { target: checkBox; property: "checked"; value: Settings.someSetting }
Binding { target: Settings; property: "someSetting"; value: checkBox.checked }
}
有时在控件中分离输入值和输出值很有用。在这种情况下,控件始终显示实际值,它也可以向用户显示延迟。
CheckBox {
checked: Settings.someSetting
onClicked: Settings.someSetting = !checked
}
我更喜欢这个解决方案
// 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
。这意味着你仍然得到一个绑定循环,但只有一个,而不是无限循环。当 if check returns 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 信号和连接来执行此操作,但代码的复杂性让我想投掷,更糟糕的是,您可能会在不需要时强制创建委托。