Connecting multiples signal/slot in a for loop in pyqt

本小妞迷上赌 提交于 2019-12-04 02:01:51

问题


I'm connecting multiple signal/slots using a for loop in PyQt. The code is bellow:

# Connect Scan Callbacks
for button in ['phase', 'etalon', 'mirror', 'gain']:
    getattr(self.ui, '{}_scan_button' .format(button)).clicked.connect(
        lambda: self.scan_callback(button))

What I expect:

  • Connect button phase_scan_button clicked signal to the scan_callback slot and send the string phase as a parameter to the slot. The same for etalon, mirror and gain.

What I'm getting:

  • For some reason my functions is always passing the string gain as parameter for all the buttons. Not sure if I'm being stupid (likely) or it is a bug.

For reference, the slot method:

def scan_callback(self, scan):
    print(scan) # Here I always get 'gain'
    if self.scanner.isWorking:
        self.scanner.isWorking = False
        self.scan_thread.terminate()
        self.scan_thread.wait()
    else:
        self.scanner.isWorking = True
        self.scan_thread.start()
        getattr(self.ui, '{}_scan_button' .format(
            scan)).setText('Stop Scan')
        getattr(self, '_signal{}Scan' .format(scan)).emit()

回答1:


My preferred way of iterating over several widgets in pyqt is storing them as objects in lists.

myButtons = [self.ui.phase_scan_button, self.ui.etalon_scan_button,
             self.ui.mirror_scan_button, self.ui.gain_scan_button]
for button in myButtons:
    button.clicked.connect(lambda _, b=button: self.scan_callback(scan=b))

If you need the strings "phase", "etalon", "mirror" and "gain" separately, you can either store them in another list, or create a dictionary like

myButtons_dict = {"phase": self.ui.phase_scan_button,
                 "etalon": self.ui.etalon_scan_button,
                 "mirror": self.ui.mirror_scan_button,
                 "gain": self.ui.gain_scan_button}

for button in myButtons_dict:
    myButtons_dict[button].clicked.connect(lambda: _, b=button self.scan_callback(scan=b))

Note, how I use the lambda expression with solid variables that are then passed into the function self.scan_callback. This way, the value of button is stored for good.




回答2:


Your lambdas do not store the value of button when it is defined. The code describing the lambda function is parsed and compiled but not executed until you actually call the lambda. Whenever any of the buttons is clicked, the current value of variable button is used. At the end of the loop, button contains "gain" and this causes the behaviour you see.

Try this:

funcs = []
for button in ['phase', 'etalon', 'mirror', 'gain']:
    funcs.append( lambda : print(button))

for fn in funcs:
  fn()

The output is:

gain
gain
gain
gain

Extending the example, as a proof that the lambdas don't store the value of button note that if button stops existing, you'll have an error:

del button
for fn in funcs:
  fn()

which has output

funcs.append( lambda : print(button))
NameError: name 'button' is not defined



回答3:


As noted here : Connecting slots and signals in PyQt4 in a loop Using functools.partial is a nice workaround for this problem.

Have been struggling with same problem as OP for a day.



来源:https://stackoverflow.com/questions/46300229/connecting-multiples-signal-slot-in-a-for-loop-in-pyqt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!