1
[ ]All1           [ ]All2        

[ ]checkbox1A     [ ]checkbox1B

[ ]checkbox2A     [ ]checkbox2B

根据上面的图表,需要做一些事情:

  1. All 复选框仅影响其所在列的开/关,并检查该列中所有复选框的开/关。
  2. 所有复选框成对工作,所以如果 checkbox1A 是开/关,checkbox1B 需要开/关
  3. 如果选中了 All 复选框,然后用户继续选中列中的一个或多个复选框,则 All 复选框应取消选中,但所有已选中的复选框应保持选中状态。

所以实际上这更像是连锁反应设置。如果复选框 All1 处于打开状态,则 chieckbox1A 和 2A 将处于打开状态,并且因为它们处于打开状态,所以 checkbox1B 和 2B 也处于打开状态,但复选框 All2 保持关闭状态。我尝试根据此逻辑连接信号,但只有配对逻辑 100% 有效。All 复选框逻辑仅在 50% 的情况下有效,而且不准确,如果不关闭所有已选中的复选框,我无法关闭 All 复选框。

真的真的很需要帮助...TT

示例代码:

cbPairKeys = cbPairs.keys()
    for key in cbPairKeys:
        cbOne = cbPairs[key][0][0]
        cbTwo = cbPairs[key][1][0]
        cbOne.stateChanged.connect(self.syncCB)
        cbTwo.stateChanged.connect(self.syncCB)

def syncCB(self):               
    pairKeys = cbPairs.keys()
    for keys in pairKeys:
        cbOne = cbPairs[keys][0][0]
        cbOneAllCB = cbPairs[keys][0][4]
        cbTwo = cbPairs[keys][1][0]
        cbTwoAllCB = cbPairs[keys][1][4]

        if self.sender() == cbOne:
            if cbOne.isChecked() or cbTwoAllCB.isChecked():
                cbTwo.setChecked(True)
            else:
                cbTwo.setChecked(False)
        else:
            if cbTwo.isChecked() or cbOneAllCB.isChecked():
                cbOne.setChecked(True)
            else:
                cbOne.setChecked(False) 

编辑

感谢用户 Avaris 的帮助和耐心,我能够将代码简化为更简洁的代码,并且 100% 的时间在第一个和第二个期望的行为上工作:

#Connect checkbox pairs     
cbPairKeys = cbPairs.keys()
    for key in cbPairKeys:
        cbOne = cbPairs[key][0][0]
        cbTwo = cbPairs[key][1][0]
        cbOne.toggled.connect(cbTwo.setChecked)
        cbTwo.toggled.connect(cbOne.setChecked) 

#Connect allCB and allRO signals    
cbsKeys = allCBList.keys()
    for keys in cbsKeys:
        for checkbox in allCBList[keys]:
            keys.toggled.connect(checkbox.setChecked)

当用户现在有选择地关闭模块化复选框时,只需要关闭 All 复选框的帮助

4

2 回答 2

3

如果我了解您的数据结构,我有一个解决方案。如果我错了,请纠正我:allCBList是一个dict(令人困惑的名字!:))。它的键是all*复选框。一个值allCBList[key]list与该复选框关联的all复选框。对于您的示例结构,它将是这样的:

{ All1 : [checkbox1A, checkbox1B],
  All2 : [checkbox2A, checkbox2B]}

然后你需要做的是:当一个复选框被切换并且它处于状态时,如果所有其他复选框都处于checked状态,那么你需要检查该复选框。否则它将被取消选中。All*checked

for key, checkboxes in allCBList.iteritems():
    for checkbox in checkboxes:
        checkbox.toggled.connect(lambda checked, checkboxes=checkboxes, key=key: key.setChecked(checked and all(checkbox.isChecked() for checkbox in checkboxes))

我想,这个陈述需要一点解释:

lambda checked, checkboxes=checkboxes, key=key:

lambda创建连接到信号的可调用对象。toggled传递复选框状态,并将其传递给checked变量。checkboxes=checkboxeskey=key部分将当前值传递给lambdacheckboxeskey参数。(你需要这个,因为 s 中的闭包lambda

接下来是:

key.setChecked(...)

我们正在设置相应复选框的checked状态。在这里面:keyAll*

checked and all(checkbox.isChecked() for checkbox in checkboxes)

allTrue如果里面的所有东西都是True,我们检查每个checkboxs 状态。True如果全部都是checked(即isChecked()返回True),这将返回。

checked and ...部分是用来短路的all。如果当前复选框变为unchecked,那么我们不需要检查其他复选框。All*将是unchecked

PS:顺便说一句,你不需要.keys()adict来迭代键。你可以迭代dict它,它会迭代它的keys。)

编辑All*:为了避免通过单击任何子复选框切换复选框的连锁反应,有必要将All*复选框的信号更改为clicked,而不是toggled。因此,只有在用户交互的情况下,All*复选框才会影响它们下面的其他复选框。

最后,您修改后的代码将是:

# Connect checkbox pairs
# you just use the values
# change 'itervalues' to 'values' if you are on Python 3.x
for cbPair in cbPairs.itervalues():
    cbOne = cbPair[0][0]
    cbTwo = cbPair[1][0]
    cbOne.toggled.connect(cbTwo.setChecked)
    cbTwo.toggled.connect(cbOne.setChecked) 

# Connect allCB and allRO signals
# change 'iteritems' to 'items' if you are on Python 3.x
for key, checkboxes in allCBList.iteritems():
    for checkbox in checkboxes:
        key.clicked.connect(checkbox.setChecked)
        checkbox.toggled.connect(lambda checked, checkboxes=checkboxes, key=key: key.setChecked(checked and all(checkbox.isChecked() for checkbox in checkboxes))
于 2012-09-12T02:01:43.170 回答
0

您的问题是您的复选框正在连接toggled信号在连接的插槽中切换它们的状态,因此再次发出信号(因此插槽再次执行......)并且您会得到不可预测的结果。显然这不是你想要的行为。您可以通过多种方式修复它:

  • 通过断开插槽开头的信号并在末尾再次连接它们
  • 通过使用一些巧妙的代码来控制信号的重新发射(我认为这是 Avari 的代码以非常紧凑的方式所做的,但我不完全确定)
  • 通过使用clicked信号,因为当复选框状态更改时它不会重新发出

您采用哪种方法取决于您。以下代码使用第三种方法:

    self.cbPair = {}
    self.cbPair['0'] = (QtGui.QCheckBox('all1', parent), 
        QtGui.QCheckBox('all2', parent))
    self.cbPair['1'] = (QtGui.QCheckBox('1a', parent), 
        QtGui.QCheckBox('1b', parent))
    self.cbPair['2'] = (QtGui.QCheckBox('2a', parent), 
        QtGui.QCheckBox('2b', parent))

    for v in self.cbPair.values():
        for cb in v:
            cb.clicked.connect(self.updateCB)

def updateCB(self):
    cb = self.sender()
    is_checked = cb.isChecked()
    id = str(cb.text())
    try:
        # Update a whole column
        column = int(id[-1]) - 1
        rows = ('1', '2')
    except ValueError:
        # Update a row and the headers row
        rows = (id[0], )
        column = {'a': 1, 'b': 0}.get(id[-1])
        if not is_checked:
            for c in (0, 1):
                self.cbPair['0'][c].setChecked(is_checked)
    for r in rows:
        self.cbPair[r][column].setChecked(is_checked)

请注意,我使用复选框文本作为 UID 来自哪一行并计算列值。如果您想为您的复选框使用不同的文本标签,您可能需要将 UID 设置为每个复选框的属性。

于 2012-09-11T22:26:43.547 回答