【问题标题】:Qt 5.4/Qml: Prevent binding loopQt 5.4/Qml:防止绑定循环
【发布时间】:2015-01-31 10:44:49
【问题描述】:

我有一个包含应用程序设置的全局单例“设置”。当我尝试运行以下代码时,我得到一个 QML CheckBox: Binding loop detected for property "checked":

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

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

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

问候,

【问题讨论】:

  • 如果您不想进行绑定循环 - 不要进行绑定。例如,使用代理变量。
  • 您的意思是复选框之外的属性,该属性包含值?

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


【解决方案1】:

不要绑定它。因为复选框并不完全依赖于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 信号并显式更改复选框。仅在程序/widget/dialog/xxx 完成后将someSettingCheckBox 的值提交给Settings

CheckBox { id: someSettingCheckBox }

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

【讨论】:

    【解决方案2】:

    我更喜欢这个解决方案

    // 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 返回 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 信号和连接来执行此操作,但代码复杂性让我想投掷,更糟糕的是,您可能会在不需要时强制创建委托。

    【讨论】:

    • 这是我们所做的,但由于某种原因,我还是遇到了绑定循环检测错误
    • 这很奇怪。我在一个活动且最新的项目中有几个这样的属性,该项目正在运行而没有绑定循环错误。我会怀疑支票中的错字或某种有趣的信号播放。也许 someSettingChanged 在其他地方触发了 checkValue 状态的反转。
    【解决方案3】:

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

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

    【讨论】:

    • 有趣的是,这在大多数情况下都有效,但在 Qt Quick Controls 2 中,这有时也会导致绑定循环。
    【解决方案4】:

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

    CheckBox {
        id: checkBox
    
        Binding { target: checkBox; property: "checked"; value: Settings.someSetting }
        Binding { target: Settings; property: "someSetting"; value: checkBox.checked }
    }
    

    【讨论】:

    • 这是一个 4 年前的答案,但我今天尝试了这个,它导致了绑定循环。可能是更新了一些东西。我正在使用控件 2。
    • 如果目标 (Settings.someSetting) 属性通过值检查正确实现,结果仍然相同。
    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 2017-04-13
      • 2015-12-07
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多