0

I try to use lambda functions to call a method that take arguments from slot that do not provide any (QtWidgets.QGroupBox.clicked() just fires without emitting anything).

I construct several QtWidgets.QGroupBox automatically for several light types. Each one should toggle on/off a sort of lighting in a 3D environment. This toggling is to be driven by the check of the groupBox for the lightType, hence this slot should pass its type and state.

This is a simplified code:

for lightType in A_CERTAIN_LIST:
    lightTypeBox = QtWidgets.QGroupBox(lightType, self.lightingDock)
    for param in a A_CERTAIN_DIC.keys():
        if param IS_THE_BOOL_PARAM:
            slotLambda = lambda: self.updateLighting(lightType, param, lightTypeBox.isChecked())
            lightTypeBox.clicked.connect(slotLambda)
...

the lightTypeBox created is also added to a dic to keep a ref of each, and all added in the end to the dockPanel it appears in.

I did hope each lightTypeBox to have its own lambda function connected, giving proper arguments (lightType, param, bool) passed to the function self.updateLighting defined in my class MainWindow(QtWidgets.QMainWindow):

    @Slot(str, str, bool)
    @Slot(str, str, int)
    @Slot(str, str, float)
    def updateLighting(self, lighting, param, val):
        self.canvas.mainRenderer.setLighting(lighting, param, val)

This has several @Slot(...) signatures for I hope to use it with others lambdas (for QspinBox, QDoubleSpinBox and so on, still to have their settings given back, for which light, for which parameters, what value...).

But What I have currently is all my group boxes on their check/uncheck do use the last slotLambda defined as if there was only but one lambda function defined, even if defined inside the second for.

What should I do differently? I did try to put lambdas in a list, in a dic, hoping to keep them separated and not rewritten at each for param pass. I don't know if it's clear...

Anyway, do you see something here?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
GregVDS
  • 13
  • 4

1 Answers1

0

Ok,

I got it:

I just defined input attributes passed to the lambda:

slotLambda = lambda lightType=lightType, param=param, lightTypeBox=lightTypeBox: self.updateLighting(lightType, param, lightTypeBox.isChecked())

So every lambda has its own arguments :-).

GregVDS
  • 13
  • 4
  • While that might work, it has a terrible readability and makes debugging very difficult. I strongly suggest you to create a specialized class of QGroupBox with a custom signal, and create each instance with the lightType and param arguments, then connect the `clicked` signal (but I *strongly* suggest you the `toggled` signal, as the checkbox could be toggled using keyboard too) to a function that emit the custom signal with the required parameters. Also, the first argument of both `clicked` and `toggled` are the checked state, so with your code above the `lightType` argument will be wrong. – musicamante Jan 23 '22 at 14:49
  • Thank you for your feedback. Yes true, I had to add state=lightTypeBox.isChecked() before all the other arguments. I know it's ugly. Currently, all my Qt code is ugly. I try to have something functional, but soon will enter into a refactoring period and all will become better organised. – GregVDS Jan 24 '22 at 06:16
  • No, using `state=state=lightTypeBox.isChecked()` as first argument is also wrong: the default value of keyword arguments is always established (and evaluated) at the time the lambda is defined, so if you use it it's wrong, if you don't it's pointless. The proper syntax should be: `lambda checked, lightType=lightType, param=param: self.updateLighting(lightType, param, checked)`. – musicamante Jan 24 '22 at 13:26
  • Thanks again to answer me back. I went in the end the subclass way with its own @Slot(PROPER_ARGS_LIST_HERE), as it is far more clear to read and maintain. – GregVDS Jan 24 '22 at 21:20