【问题标题】:Connecting multiples signal/slot in a for loop in pyqt在pyqt的for循环中连接多个信号/插槽
【发布时间】:2018-02-28 05:26:16
【问题描述】:

我在 PyQt 中使用 for loop 连接多个信号/插槽。代码如下:

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

我的期望:

  • 将按钮phase_scan_button 单击signal 到scan_callback slot 并将字符串phase 作为参数发送到slot。 etalon、mirror 和 gain 也是如此。

我得到了什么:

  • 出于某种原因,我的函数总是将字符串gain 作为所有按钮的参数传递。不知道我是在愚蠢(可能)还是这是一个错误。

供参考,slot 方法:

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()

【问题讨论】:

    标签: python qt pyqt signals-slots


    【解决方案1】:

    我在 pyqt 中迭代多个小部件的首选方式是将它们作为对象存储在列表中。

    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))
    

    如果您需要单独的字符串“phase”、“etalon”、“mirror”和“gain”,您可以将它们存储在另一个列表中,或者创建一个类似的字典

    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))
    

    注意,我如何使用带有实体变量的 lambda 表达式,然后将这些变量传递给函数self.scan_callback。这样,button 的值就会被永久保存。

    【讨论】:

    • 请注意 GPhilo 的回答。他指出了您方法的问题,而我的方法仅帮助您管理小部件和名称!
    • 我做到了。两个都是很好的答案,真的很难选择接受哪一个
    • @offeltoffel 我很好奇……这与 OP 所做的尝试有何不同?您仍在定义一个使用 button 值的 lambda,理论上应该在运行时对其进行评估。为什么不是这样?
    • @offeltoffel 您上次编辑的技巧不错,尽管我会简单地保留self.scan_callback(b),因为指定scan=b 是多余的。 @Eduardo,如果您想知道为什么这适用于看似微小的变化,have a look at this amazing question and its answer.。
    • @GPhilo:我认为你所说的冗余更多是一个品味问题。当然self.scan_callback(b) 与self.scan_callback(scan=b) 相同,但我个人喜欢跟踪将哪个值传递给函数的哪个参数。当有 3 个或更多变量要传递时,它会变得很糟糕。
    【解决方案2】:

    您的 lambdas 不在定义时存储 button 的 值。描述 lambda 函数的代码会被解析和编译,但在您实际调用 lambda 之前不会执行。 每当单击任何按钮时,都会使用 变量的当前值 button。在循环结束时,button 包含 "gain",这会导致您看到的行为。

    试试这个:

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

    输出是:

    gain
    gain
    gain
    gain
    

    扩展示例,作为 lambda 不存储 button 值的证明,请注意,如果 button 不再存在,您将遇到错误:

    del button
    for fn in funcs:
      fn()
    

    有输出

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

    【讨论】:

    • 非常感谢您的回答,我现在已经很清楚了。但是,我选择了@offeltoffel 的答案,因为他提出了解决方案。如果可以的话,我肯定会接受这两个作为正确答案。
    【解决方案3】:

    如此处所述:Connecting slots and signals in PyQt4 in a loop 使用functools.partial 是解决此问题的好方法。

    已经和 OP 一样的问题苦苦挣扎了一天。

    【讨论】:

      猜你喜欢
      • 2011-08-16
      • 2019-07-25
      • 1970-01-01
      • 2018-09-23
      相关资源
      最近更新 更多