PDA

View Full Version : Event problem on Dynamically created combo box in tablewidget



chistof
17th June 2015, 08:28
Hi,

I have a QTableWidget, in which I create dynamically some QComboBox. I simplified the code so things might be missing but the important part is in there, it looks like this :



def testPrint(self, index):

print index


def itemComboBox(self, array, curIndex):

comboBox = QtGui.QComboBox(self)
comboBox.addItems(array)
comboBox.setCurrentIndex(curIndex)

return comboBox


def createTable(self):

tw = self.ui.tableWidget

version = [ 'v01', 'v02', 'v03' ]

cb = []
for i range(0,len(version)):

cb.append(self.itemComboBox(version,'v01'))
tw.setCellWidget(i, 0, cb[i])


So no problem with that, it creates what I need to see and when I go through each rows I get the values I want. However, I'm trying to run a function when the comboBox value get changed. I first tried that :



def createTable(self):

tw = self.ui.tableWidget

version = [ 'v01', 'v02', 'v03' ]

cb = []
for i range(0,len(version)):

cb.append(self.itemComboBox(version,'v01'))
cb[i].currentIndexChanged.connect( lambda: self.testPrint(i) ) ###LINE###
tw.setCellWidget(i, 0, cb[i])


But with this, for some reason, each combobox returns the value from the last in the list, seems like I keep overriding the event in the loop but I don't see how since I use a list with index. So as a test I quickly tried something simple outside of the loop after getting rid of the line ###LINE###



for k in range(0,len(version)) :
cb[k].currentIndexChanged.connect( lambda: self.testPrint(k) )


Which still doesn't work, then because I was really suspicious I tried that instead:



cb[0].currentIndexChanged.connect( lambda: self.testPrint(0) )
cb[1].currentIndexChanged.connect( lambda: self.testPrint(1) )
cb[2].currentIndexChanged.connect( lambda: self.testPrint(2) )


And that works ! I really don't get why going through a loop would not work the same way.

Anyone can save me on this one ?

Cheers
Chris

yeye_olive
17th June 2015, 14:37
This problem is not related to Qt, but to the way Python captures variables in a closure. Your lambda refers to the variable i defined in the enclosing loop, and Python's semantics bind it to the variable's name and scope, not to its value at the time the closure is created. In other words, i is evaluated when the lambda is executed, not when it is constructed, therefore all lambdas see the last value of i.

I found a Stack Overflow discussion with possible solutions:
http://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture-in-python

By contrast, C++ lets you choose how variables are captured, just like ordinary function parameters (by value or reference).

chistof
18th June 2015, 06:58
Thanks !

Reading this made me understood the issue and I actually sorted it out pretty simply :




def testPrint(self, index):

print index


def itemComboBox(self, array, curIndex, ID):

comboBox = QtGui.QComboBox(self)
comboBox.addItems(array)
comboBox.setCurrentIndex(curIndex)
comboBox.currentIndexChanged.connect( lambda: self.testPrint(ID) )

return comboBox


def createTable(self):

tw = self.ui.tableWidget

version = [ 'v01', 'v02', 'v03' ]

for i range(0,len(version)):

cb = self.itemComboBox(version,'v01',i)
tw.setCellWidget(i, 0, cb)


By passing the i variable when creating the combo box and creating the event in itemComboBox function itself it sorts out the problem.

Thanks again :D

Chris