1

I am creating a number of widgets inside a layout, and every widget is passing a signal (current number rows). But, suerprisingly, the output is not a number (row_count), but False?

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

class main(QWidget):
    def __init__(self):
        super().__init__()
        
        top_layout = QFormLayout()
        self.setLayout(top_layout)
        
        test = QToolButton()
        test.setText('W_1')
        top_layout.addRow(test)
        test.clicked.connect(lambda val=top_layout.rowCount(): self.delNewPar(val))

        test2 = QToolButton()
        test2.setText('W_2')
        top_layout.addRow(test2)
        test2.clicked.connect(lambda val=top_layout.rowCount(): self.delNewPar(val))

        test3 = QToolButton()
        test3.setText('W_3')
        top_layout.addRow(test3)
        test3.clicked.connect(lambda val=top_layout.rowCount(): self.delNewPar(val))
        
    @pyqtSlot(int)
    def delNewPar(self, val):
        print(val)        
   
if __name__ == "__main__":
    app = QApplication(sys.argv)
    widget = main()
    widget.show()
    sys.exit(app.exec_())      

The output when click different buttons is:

False
False
False
False
False
False

What am I missing in this procedure ?

This is a working example (same logic I am following:)

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QAction, QPushButton,
    QWidget, QLabel, QVBoxLayout, QHBoxLayout
)
import sys

class Window(QWidget):

    def __init__(self):
        super().__init__()

        v = QVBoxLayout()
        h = QHBoxLayout()

        for a in range(10):
            button = QPushButton(str(a))
            button.pressed.connect(
                lambda val=a: self.button_pressed(val)
            )                   # pass the value in as a (re-)named parameter.
            h.addWidget(button)

        v.addLayout(h)
        self.label = QLabel("")
        v.addWidget(self.label)
        self.setLayout(v)

    def button_pressed(self, n):
        self.label.setText(str(n))


app = QApplication(sys.argv)
w = Window()
w.show()
app.exec_()

I am using the same logic, but my output is different !?

zezo
  • 445
  • 4
  • 16
  • The `clicked` signal has a default *checked* argument, so do `test.clicked.connect(lambda checked, val=top_layout.rowCount(): ...` – ekhumoro Jan 08 '22 at 19:46
  • @ekhumoro Your comment is redundant with my answer that I wrote a full hour erlier. Do you really believe it is a good solution to misuse a parameter default for passing context to a lambda? – ypnos Jan 08 '22 at 20:10
  • @ypnos It's a [common python idiom](https://stackoverflow.com/q/19837486/984421). As written, your proposed solution fails with both the OP's examples. For the first one, you'd need to use a different variable name for each connection, otherwise the last value of `val` will be used (because a closure is formed over it, the same as with `self`). For the second one, your answer won't work at all (hence the above-mentioned idiom). This sort of question has been asked and answered many times on SO, which is why it has been closed as a duplicate. – ekhumoro Jan 08 '22 at 21:17
  • Just to be totally clear: the difference between the two examples is that the first one uses [clicked](https://doc.qt.io/qt-5/qabstractbutton.html#clicked) (which passes a default argument), whilst the second one uses [pressed](https://doc.qt.io/qt-5/qabstractbutton.html#pressed) (which does not pass anything). – ekhumoro Jan 08 '22 at 21:27
  • @ekhumoro, yes it is working nicely. Thanks for the explanation – zezo Jan 08 '22 at 22:42
  • @ekhumoro I also understand using several local variables is not pretty code. I am surprised that this hack is a common idiom, good to know, I learned something new then! – ypnos Jan 09 '22 at 12:16
  • 1
    @ypnos TBF, although it's a common idiom, python does provide [functools.partial](https://docs.python.org/3/library/functools.html#functools.partial), which many people consider to be a cleaner alternative. Also, [some consideration has been given](https://www.mail-archive.com/python-ideas@python.org/msg25069.html) to changing the way for-statements work so that they capture their loop variables. However, for many people, I think it's a case of Practicality Beats Purity, so I doubt this little "feature" will ever completely disappear - there's just too much code out there that relies on it. – ekhumoro Jan 09 '22 at 17:14

0 Answers0