Pyqt4 hooks up QCheckbox state correctly

I have a class QWidget

that contains QLabel

. The class generates QCheckbox

' es in QVBox

. I am trying to wire each checkbox to a method nameCheckBox

that will update QLabel

to display the title of the last checked box. However, when a checkbox is effectively disabled / checked, it is always defined as Unchecked. Also the returned name is always the last checkbox created. I don't understand where my mistake is. Here is my code:

import sys
from PyQt4 import QtCore
from PyQt4.QtGui import *
from MenusAndToolbars import MenuWindow

class checkBoxWidget(QWidget):
    """
    This widget has a QVBox which contains a QLabel and QCheckboxes.
    Qcheckbox number is connected to the label.
    """

    def __init__(self):
        QWidget.__init__(self)
        self.__setUI()

    def __setUI(self):
        vbox = QVBoxLayout(self)
        label = QLabel('Last clicked button: ' + "None", self)

        vbox.addWidget(label)

        listCB = []

        for i in range(10):
            listCB.append( QCheckBox('CheckBox Nb. ' + str(i+1) ) )
            listCB[i].stateChanged.connect(lambda: self.nameCheckBox(label,  listCB[i]) )
            vbox.addWidget( listCB[i] )


    def nameCheckBox(self, label, checkBox):
        if checkBox.isChecked():
            print "Checked: " + checkBox.text()
            label.setText('Last clicked button: ' + checkBox.text())
        else:
            print "Unchecked: " + checkBox.text()



def main():
    app = QApplication(sys.argv)
    window = QMainWindow()
    window.setCentralWidget( checkBoxWidget() )
    window.show()
    #window = WidgetWindow()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

      


EDIT 1

I have found several "hacked" solutions.

SOLUTION 1: Creating a callBack function does the trick:

def callBack(self, list, index, label):
    return lambda: self.nameCheckBox(label, list[index])

      

Then I connect the signal QCheckbox().stateChanged

like this:

listCB[i].stateChanged.connect( self.callBack(listCB, i, label) )

      

SOLUTION 2: using a module partial

:

First, we import the module:

from functools import partial

      

Then the signal connection is done as follows:

listCB[i].stateChanged.connect( partial( self.nameCheckBox, label, listCB[i] ) )

      

However, I would like to use the lambda expression on one line. I would especially like to understand how it works. By following links I realized that the problem is with the lambda volume. As Oleg Prypin advised me, I wrote:

listCB[i].stateChanged.connect(lambda i=i: self.nameCheckBox(label,  listCB[i]) )

      

Here the variable i

is new. However, my original problem remains. Then I tried this out of curiosity:

listCB[i].stateChanged.connect( lambda label=label, listCB=listCB, i=i: self.nameCheckBox(label, listCB[i] ) )

      

But I am getting the following error:

Traceback (most recent call last):
Checked: CheckBox Nb. 2
  File "Widgets.py", line 48, in <lambda>
    listCB[i].stateChanged.connect( lambda label=label, listCB=listCB, i=i: self.nameCheckBox(label, listCB[i] ) )
  File "Widgets.py", line 59, in nameCheckBox
    label.setText('Last clicked button: ' + checkBox.text())
AttributeError: 'int' object has no attribute 'setText'

      

Here, the correct button seems to be recognized when un / checked. However, it seems like the new variable is label

being treated as an int? What's going on here?

+3


source to share


1 answer


lambda: self.nameCheckBox(label,  listCB[i])

      

is bound to a variable i

, which means that the value i

will be the same as when the lambda was called, not when it was created, which in this case is always 9.
Possible fix:

lambda i=i: self.nameCheckBox(label, listCB[i])

      

There is a lot of general information on this topic. Starting points: Google search , another question Creating a lambda inside a loop .




Unfortunately my fix doesn't work because this signal provides an argument to the checked

called function, overriding this default argument from 0 or 2 depending on the validation state. This will work (ignore the unwanted argument):

lambda checked, i=i: self.nameCheckBox(label, listCB[i])

      




And here's an alternative way to write this class:

class CheckBoxWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.setupUi()

    def setupUi(self):
        vbox = QVBoxLayout(self)
        self.label = QLabel("Last clicked button: None")

        vbox.addWidget(self.label)

        for i in range(10):
            cb = QCheckBox("CheckBox Nb. " + str(i+1))
            cb.stateChanged.connect(self.nameCheckBox)
            vbox.addWidget(cb)

    def nameCheckBox(self, checked):
        checkBox = self.sender()
        if checked:
            print("Checked: " + checkBox.text())
            self.label.setText("Last clicked button: " + checkBox.text())
        else:
            print("Unchecked: " + checkBox.text())

      

+2


source







All Articles