PyQt - 如何将多个信号连接到同一个小部件

时间:2012-09-11 19:02:46

标签: python pyqt4

[ ]All1           [ ]All2        

[ ]checkbox1A     [ ]checkbox1B

[ ]checkbox2A     [ ]checkbox2B

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

  1. 全部复选框仅影响其所在列的开/关,并检查/关闭该列中的所有复选框。
  2. 所有复选框都成对使用,因此如果checkbox1A打开/关闭,则复选框1B需要打开/关闭
  3. 如果选中了All复选框,然后用户继续选中列中的一个或多个复选框,则应取消选中All复选框,但应检查已选中的所有复选框。
  4. 所以这更像是连锁反应设置。如果选中复选框All1,则chieckbox1A和2A将打开,并且因为它们已打开,checkbox1B和2B也打开,但复选框All2保持关闭状态。我尝试根据这个逻辑连接信号,但只有配对的逻辑100%工作。全部复选框逻辑仅在50%的时间内工作,而且不准确,并且我没有办法关闭全部复选框而不关闭所有已经选中的复选框。

    真的需要帮助...... T-T

    示例代码:

    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)
    

    当用户选择性地关闭模块化复选框时,仅需要关闭“全部”复选框的帮助

2 个答案:

答案 0 :(得分:2)

如果我了解您的数据结构,我有一个解决方案。如果我错了,请纠正我:allCBListdict(令人困惑的名字!:))。它的键是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部分将当前值传递给lambda的checkboxeskey参数。 (由于lambda s)

中的闭包,你需要这个

接下来:

key.setChecked(...)

我们将checked状态key设置为相应的All*复选框。在里面:

checked and all(checkbox.isChecked() for checkbox in checkboxes)
如果内部的所有内容都是all,则{p> TrueTrue,我们会检查每个checkbox的状态。如果所有内容都为True(即checked返回isChecked()),则会返回True

checked and ...部分是为了使all短路。如果当前复选框变为unchecked,则我们无需检查其他复选框。 All*将是unchecked

PS :顺便说一下,你不需要.keys() dict来迭代密钥。你可以迭代{{1它将迭代dict。)

修改:为了避免因点击任何子复选框切换keys复选框而产生连锁反应,有必要将All*复选框的信号更改为{{1}而不是All*。因此,在用户互动的情况下,clicked复选框会影响其下方的其他

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

toggled

答案 1 :(得分: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来自行,并计算colum值。如果要为复选框使用不同的文本标签,可能需要将UID设置为每个复选框的属性。