PyQt - 如何将多个信号连接到同一个小部件
Posted
技术标签:
【中文标题】PyQt - 如何将多个信号连接到同一个小部件【英文标题】:PyQt - How to connect multiple signals to the same widget 【发布时间】:2012-09-11 19:02:45 【问题描述】:[ ]All1 [ ]All2
[ ]checkbox1A [ ]checkbox1B
[ ]checkbox2A [ ]checkbox2B
根据上面的图表,需要做一些事情:
-
所有复选框仅影响其所在列的开/关,并检查该列中所有复选框的开/关。
所有复选框都成对工作,因此如果 checkbox1A 开启/关闭,则 checkbox1B 需要开启/关闭
如果选中了“全部”复选框,然后用户继续选中该列中的一个或多个复选框,则“全部”复选框应取消选中,但所有已选中的复选框应保持选中状态。
所以实际上这更像是连锁反应设置。如果复选框 All1 处于打开状态,则 chieckbox1A 和 2A 将处于打开状态,并且因为它们处于打开状态,所以 checkbox1B 和 2B 也处于打开状态,但复选框 All2 保持关闭状态。我尝试根据此逻辑连接信号,但只有配对逻辑 100% 有效。 All 复选框逻辑仅在 50% 的情况下有效,而且不准确,如果不关闭所有已选中的复选框,我无法关闭 All 复选框。
真的很需要帮助……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)
当用户现在有选择地关闭模块化复选框时,只需要关闭“全部”复选框的帮助
【问题讨论】:
你的代码在哪里?您应该提供一个小的、独立的示例来说明问题。 【参考方案1】:如果我了解您的数据结构,我有一个解决方案。如果我错了,请纠正我:allCBList
是dict
(名称混淆!:))。它的键是all*
复选框。而allCBList[key]
的值是一个list
,其复选框与all
复选框相关联。对于您的示例结构,它将是这样的:
All1 : [checkbox1A, checkbox1B],
All2 : [checkbox2A, checkbox2B]
那么你需要做的是:当一个复选框被切换并且它处于checked
状态时,如果所有其他复选框都处于checked
状态,那么你需要检查All*
复选框。否则将被取消选中。
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=checkboxes
和 key=key
部分将当前值传递给 lambda 的 checkboxes
和 key
参数。 (因为lambda
s 的关闭,你需要这个)
接下来是:
key.setChecked(...)
我们正在设置key
的checked
状态,这是适当的All*
复选框。在这里面:
checked and all(checkbox.isChecked() for checkbox in checkboxes)
all
是 True
,如果里面的所有内容都是 True
,我们检查每个 checkbox
s 状态。如果所有都是checked
,这将返回True
(即isChecked()
返回True
)。
checked and ...
部分用于短路all
。如果当前复选框变成unchecked
,那么我们就不需要勾选其他的了。 All*
将是 unchecked
。
(PS:顺便说一句,你不需要得到dict
的.keys()
来迭代键。你可以只迭代dict
,它会迭代超过其keys
。)
编辑:为了避免通过单击任何子复选框切换All*
复选框的连锁反应,有必要将All*
复选框的信号更改为clicked
,而不是@987654366 @。因此,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))
【讨论】:
所以我尝试了这种方法,当一个子复选框被选中时,All* 复选框也被选中,但连锁反应也发生在其他仍然选中的子复选框上。在 All* 复选框被选中后,仍然选中的子复选框都被选中,在 All* 复选框之后 我也想感谢你在这个问题上帮助我。如果没有您的耐心和有用的提示和技巧,该工具永远不会走到这一步。非常感谢! @Orchainu:哦,对了! :),忘记了。修复很容易。我们只需要使All*
复选框仅通过用户操作影响其他人。那将是clicked
信号而不是toggled
。我要编辑我的答案。【参考方案2】:
您的问题是您的复选框正在连接toggled
信号并在您连接的插槽中切换它们的状态,以便再次发出信号(因此插槽再次执行......)并且您得到不可预知的结果。显然这不是你想要的行为。您可以通过多种方式修复它:
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 设置为每个复选框的属性。
【讨论】:
以上是关于PyQt - 如何将多个信号连接到同一个小部件的主要内容,如果未能解决你的问题,请参考以下文章