Solved transmitting extra data with signals that already sends extra data
-
@efremdan1 said in transmitting extra data with signals that already sends extra data:
lambda val=a: self.button_pressed(val)
Try:
lambda checked, val=a: self.button_pressed(val)
?
Assuming that works(!), I think the Python lambda rule for slots is:
- First specify as many parameters as the signal sends. You do not have to actually use these in the lambda body if you are not interested in them, but you must specify them to match up to what the signal sends. (You may omit these iff you neither use them nor want to add your own extra parameters.)
- After these you can then specify as many extra
a=b
s as you wish to pass your additional parameters.
-
@JonB said in transmitting extra data with signals that already sends extra data:
Try:
lambda checked, val=a: self.button_pressed(val)Doesn't work. Upon clicking, Python sends the error:
TypeError: <lambda>() missing 1 required positional argument: 'checked'
Any other ideas?
-
@efremdan1
I will check this out soon in Python and replace this answer with the correction shortly.... -
@efremdan1 said in transmitting extra data with signals that already sends extra data:
Doesn't work. Upon clicking, Python sends the error:
TypeError: <lambda>() missing 1 required positional argument: 'checked'
Oh, but it does work! You asked for
I want to use the
clicked
signal instead of thepressed
signal.However, only
clicked
signal/slot takes thatchecked
parameter, notpressed
signal. And you have left in your originalbutton.pressed.connect(lambda ...)
instead of changing tobutton.clicked.connect(lambda ...)
, haven't you? :) -
No I didn't. Here's the code that doesn't work:
from PySide2.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.clicked.connect( lambda checked, val=a: self.button_pressed(val) ) 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_()
That works for you? I'm using Python 3.9.0 and PySide2 5.15.2. What versions are you using that has that working?
-
@efremdan1
Mine almost same, yep. I can't spot what/if you have different. Mine works :) If I deliberately change tobutton.pressed.connect
I get just your error, so seemed likely you'd still be there too!I'll have to look tomorrow. I'll try copy/paste your code. Remind me :)
-
@efremdan1
Hi.First I checked yours and agree it does give the
TypeError: <lambda>() missing 1 required positional argument: 'checked'
error message.Then I looked a lot mine, frowning to see the difference, because mine does work, and only correctly gives just that error if I connect to, say,
pressed
signal which does not have thechecked
argument. Until I suddenly noticed that mine had# from PySide2 import ...
commented out andfrom PyQt5 import ...
instead, using trusty & robust PyQt5.... (And I also tested yours with PyQt5 instead and, surprise, surprise, yours then works too.)So now what we have is a difference in behaviour for PySide2 compared against PyQt5 :( I will now have a look around/play to see if I can see what we are supposed to do in PySide2 for this which actually works....
-
@efremdan1
OK, so my first attempt to "correct" this works, under PySide2 as well as PyQt5:lambda rubbish=0, val=a: self.button_pressed(val)
Note that I have assigned a value to the first parameter. I picked
rubbish
to show there is no connection between that variable name and thechecked
name in the signal. Obviously for clarity you should really writechecked=False
.Now, while this works and is hopefully good enough for you(?), I don't think it is "right". The signal has a parameter
PySide2.QtWidgets.QAbstractButton.clicked([checked=false])
and we should be able to get the passed-in value of
checked
in our lambda so that we can pass that on correctly. I don't see that we can. Note that attemptinglambda checked=checked, val=a: self.button_pressed(val)
gives
NameError: name 'checked' is not defined
in both, so that's not right.I haven't even verified whether the PyQt5
lambda checked, val=a: self.button_pressed(val)
really gets a passed-in value forchecked
, and I can writerubbish
there too and it works. So the only difference I see is that PyQt5 allows this first positional parameter without any=default
but PySide2 demands a default.I think I'll leave this to you at this point. The code I've given you is probably enough for you because you don't bother looking at the
checked
value.... -
@efremdan1
I can never leave something unsatisfactory alone... :(BTW, the accepted solution at https://stackoverflow.com/questions/38090345/pass-extra-arguments-to-pyqt-slot-without-losing-default-signal-arguments is worth reading. Using PyQt5 at least, you can see he shares my understanding of how we should add
checked
as first parameter. Note thatfunctools.partial
is another way which might work right in PySide2.I have just found exactly your issue reported from PySide2: https://forum.learnpyqt.com/t/getting-typeerror-lambda-missing-1-required-positional-argument-checked/586
button.clicked.connect(lambda checked, a=a: self.button_clicked(a))
TypeError: () missing 1 required positional argument: ‘checked’.
I use PySide2 and Python 3.8The response there of needing to use
QSignalMapper
is irrelevant, but see again https://stackoverflow.com/a/52283813/489865I am not finding a satisfactory answer for the behaviour exhibited in PySide2, and at the moment see it as a bug.
-
Thank you so much @JonB! You're correct that the solution you gave works satisfactorily for me.