QtCore.QObject.connect in a loop only affects the last instance

Question:

I have got a loop. I created a QCheckBox and put it in a QTableWidget cell, and everything is Ok. In each step of loop I have called a connect function, for myslot SLOT, but only the last QCheckBox instance is applied. I googled a lot and have found many people have my problem. I have applied their solutions, but my problem remains.

for row in xrange(len(uniqueFields)):
    instance = QtGui.QCheckBox(uniqueFields[row], findInstance.tableWidget)
    print QtCore.QObject.connect(instance,
        QtCore.SIGNAL(_fromUtf8("stateChanged (int)")),
        lambda: findInstance.projectsInstance.myslot(
                    "TWCH", findInstance, instance.text(),
                    instance.checkState(), instance))
    findInstance.tableWidget.setRowCount(findInstance.tableWidget.rowCount() + 1)
    findInstance.tableWidget.setCellWidget(row, 0, instance)

Note: my connect function return True.

How to create connect function in a loop that enumerates all of the instances?

Asked By: zoyak

||

Answers:

The problem is that you are creating a function using lambda where some of the variables inside the function are not being passed in as arguments to the function. When the lambda function is executed, when the signal is emitted, it uses the value of those variables (like instance) at that moment in time. To be clear, every lambda function you make is using the value of instance at runtime, rather than define time. So instance only holds a reference to the object used in the last iteration of our loop, which explains the behaviour you are seeing.

Some useful information can be found here (read the comments too) http://eli.thegreenplace.net/2011/04/25/passing-extra-arguments-to-pyqt-slot/

From the comments of the above link:

What you can do is have another function generate the lambda, i.e.
something like:

def make_callback(param):   
        return lambda: self.on_button(param)

And in the connection, call make_callback(i). Then a different lambda is
created for each iteration.

So you would want to generalise this and pass in things like instance to the make_callback function and then place your lambda definition inside the make_callback function. I would provide a clear example of this, but as the other answer says, your formatting appears to have become very messed up in your question and I would likely get it wrong for your specific application. If you aren’t following what I’ve said, make the code in your question clearer and I’ll have a go at creating an example!

Answered By: three_pineapples

Put the loop variable in a default argument, like this:

lambda state, instance=instance: findInstance.projectsInstance.myslot(
    "TWCH", findInstance, instance.text(), instance.checkState(), instance)

This will give each lambda its own local copy of the instance variable.

EDIT

Here’s a simple script that demonstrates how to use default lambda arguments:

from PyQt4 import QtGui

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        layout = QtGui.QVBoxLayout(self)
        for index in range(4):
            instance = QtGui.QCheckBox('Checkbox(%d)' % index, self)
            instance.stateChanged.connect(
                lambda state, instance=instance:
                    self.mySlot(instance.text()))
            layout.addWidget(instance)

    def mySlot(self, text):
        print('clicked: %s' % text)


if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
Answered By: ekhumoro

I have same problem , you should use functools.partial such as:

for key, val in a_DICT_THAT_YOU_STORED_YOUR_OBJECTS_AND_STRINGS:
    obj = partial(   findInstance.projectsInstance.myslot,arg1="TWCH",arg2=self,arg3=key,arg4=val.checkState() )
    QtCore.QObject.connect(val, QtCore.SIGNAL(_fromUtf8("stateChanged (int)")), obj)

Of course, argX should set to your real name of your argument of your function name.

Answered By: PersianGulf
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.