1

I have many QPushButtons in a QTableWidget:

from PyQt5 import QtWidgets
import sys

class Mainwindow(QtWidgets.QWidget):

    def __init__(self):
        super().__init__()
        table = QtWidgets.QTableWidget(3, 1)
        l = QtWidgets.QHBoxLayout()
        l.addWidget(table)
        self.setLayout(l)
        for i in range(3):
            btn = QtWidgets.QPushButton('button '+str(i))
            btn.clicked.connect(lambda: self.btnSlot(i))
            layout = QtWidgets.QHBoxLayout()
            layout.setContentsMargins(0,0,0,0)
            container = QtWidgets.QWidget()
            container.setLayout(layout)
            layout.addWidget(btn)
            table.setCellWidget(i, 0, container)

    def btnSlot(self, num):
        print(num)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    m = Mainwindow()
    m.show()
    app.exec()

Why is this snippet printing 2 no matter which button I push?

I would expect button 0 to print 0, button 1 to print 1 etc, since I connect to a lambda function with the loop counter i.

What do I have to change to get the behaviour I expect?

Jonas
  • 1,838
  • 4
  • 19
  • 35

1 Answers1

1

It's not working because the i passed as a parameter to btnSlot is evaluated at the time btnSlot is called, NOT at the time the lambda is define. You need to freeze the value of i using a partial function, so:

from functools import partial

btn.clicked.connect(partial(self.btnSlot,i))

Another approach is to take advantage of the fact that default parameters are evaluated at the time function is defined, NOT at call time. So:

btn.clicked.connect(lambda checked,num=i: self.btnSlot(num))
Mario Camilleri
  • 1,457
  • 11
  • 24
  • 2
    Why are you using `partial` ***and*** `lambda`? – ekhumoro Nov 14 '19 at 15:06
  • ekhumoro - you are right of course, the lambda is entirely superfluous. I suppose I just took the line from the question and amended it instead of rewriting it. Have now changed the answer. – Mario Camilleri Nov 14 '19 at 15:28