连接到stateChange事件以动态创建QCheckBoxes

时间:2017-01-26 01:09:38

标签: python-2.7 pyqt pyqt4 qcheckbox

我正在开发一个Python应用程序,它从一个包含大量来源记录的大文件中绘制数据。我试图给用户的一个选项是,如果需要,只能绘制这些源的子集的选项。我通过首先阅读文件,找出有多少独特的东西,然后为每个文件创建一个QCheckBox()来命名(每个源都有一个唯一的名称)。在这种特殊情况下,数据文件被解析为一个巨大的字典,其中键是唯一的源。我想为每个复选框连接到stateChange()事件,然后在取消选中该框时禁用该源的绘图。在这种情况下,在选中/取消选中该框时,将从源列表中添加/删除源。我遇到的问题是我的所有复选框最终都连接到列表中的最终源。

最初,创建的窗口看起来正确,每个按钮都被正确命名。每次按下按钮时,btnstate()都应该只打印与该按钮关联的文本。如果您可以显式定义每个按钮,则该方法有效,如示例中的单选按钮所示。如果单击其中一个,您将获得打印按钮的正确名称,但是当取消选中/重新检查任何复选框时,btnstate将打印“test4”。

我做错了什么?

以下是代码(源更改为虚拟值):

import sys
from PyQt4.QtGui import *

def btnstate(b):
    print b.text()

def main():
   app = QApplication([])
   widget = QWidget()
   layout = QVBoxLayout()
   widget.setLayout(layout)
   radio_layout = QHBoxLayout()
   checkbox_layout = QHBoxLayout()

   #setup radio buttons for config pop-up window
   r1 = QRadioButton("Page Count")
   r2 = QRadioButton("Date")
   r1.toggled.connect(lambda:btnstate(r1))
   r2.toggled.connect(lambda:btnstate(r2))
   radio_layout.addWidget(r1)
   radio_layout.addWidget(r2)

   cbs = []
   for idx, serial in enumerate(["test1", "test2", "test3", "test4"]):
      temp = QCheckBox(serial)
      temp.setText(serial)
      temp.setChecked(True)
      checkbox_layout.addWidget(temp)
      temp.stateChanged.connect(lambda:btnstate(temp))
      cbs.append(temp)

  layout.addLayout(radio_layout)
  layout.addLayout(checkbox_layout) 
  widget.show()
  sys.exit(app.exec_())


if __name__ == '__main__':
  main()

1 个答案:

答案 0 :(得分:0)

我认为发生这种情况的原因与Python如何绑定和解除循环内的引用有关。因为temp在每次迭代时都被重新定义,所以插槽也在被更新,因此它有效地为每个按钮调用相同的lambda。这种手工波浪,因为我对Python引用细节的理解并不是很深刻。但是我知道Python的Python绑定在Python的引用和垃圾收集方面存在很多问题,例如:删除小部件时,因为Qt对象层次结构并不完全适用于Python。

无论如何,更实际的是,有一个非常简单的解决方案。您可以使用functools.partial方法将部分函数定义为槽,而不是lambda。将按钮绑定为第一个对象,使按钮状态(作为信号参数发出)保持未绑定状态。像这样:

import functools

def btnstate(button, state):
    print button.text()

然后在循环中:

for idx, serial in enumerate(['test1', 'test2', 'test3', 'test4']):
    temp = QCheckBox(serial)
    checkbox_layout.addWidget(temp)
    temp.stateChanged.connect(functools.partial(btnstate, serial))

运行此选项,我现在可以在选中/取消选中每个框时打印正确的标签。

编辑:

请参阅this post,了解Python的引用计数与Qt对象层次结构的奇怪方式交互的另一个例子。