Python 使用lambda表达式连接pyqt中的插槽

Python 使用lambda表达式连接pyqt中的插槽,python,lambda,pyqt,Python,Lambda,Pyqt,我试图用lambda函数连接插槽,但它没有按我预期的方式工作。在下面的代码中,我成功地正确连接了前两个按钮。对于后两个,我在一个循环中连接,这是错误的。我之前的某个人也有同样的问题(),但这个解决方案对我不起作用。我已经盯着屏幕看了半个小时,但我不知道我的代码有什么不同 class MainWindow(QtGui.QWidget): def __init__(self): super(QtGui.QWidget, self).__init__() ma

我试图用lambda函数连接插槽,但它没有按我预期的方式工作。在下面的代码中,我成功地正确连接了前两个按钮。对于后两个,我在一个循环中连接,这是错误的。我之前的某个人也有同样的问题(),但这个解决方案对我不起作用。我已经盯着屏幕看了半个小时,但我不知道我的代码有什么不同

class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(QtGui.QWidget, self).__init__()

        main_layout = QtGui.QVBoxLayout(self)

        # Works:
        self.button_1 = QtGui.QPushButton('Button 1 manual', self)
        self.button_2 = QtGui.QPushButton('Button 2 manual', self)
        main_layout.addWidget(self.button_1)
        main_layout.addWidget(self.button_2)

        self.button_1.clicked.connect(lambda x:self.button_pushed(1))
        self.button_2.clicked.connect(lambda x:self.button_pushed(2))

        # Doesn't work:
        self.buttons = []
        for idx in [3, 4]:
            button = QtGui.QPushButton('Button {} auto'.format(idx), self)
            button.clicked.connect(lambda x=idx: self.button_pushed(x))
            self.buttons.append(button)
            main_layout.addWidget(button)


    def button_pushed(self, num):
        print 'Pushed button {}'.format(num)
按下前两个按钮会产生“按下按钮1”和“按下按钮2”,另外两个按钮都会产生“按下按钮错误”,尽管我预期的是3和4


我还没有完全理解lambda机制。到底是什么连接起来的?指向lambda生成的函数(在中替换了参数)的指针,或者每当信号触发时,lambda函数都会被计算?

我也不确定在这里使用lambda会出现什么问题。我认为这是因为idx(设置自动按钮时的循环索引)超出了范围,不再包含正确的值

但我不认为你需要这样做。看起来使用lambda的唯一原因是,您可以将参数传递给button_pushed(),以确定它是哪个按钮。有一个函数
sender()
,可以在按钮按下()槽中调用,该槽用于识别发出信号的按钮

这里有一个例子,我认为它或多或少地符合你的拍摄目的:

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

import sys

class MainWindow(QWidget):
    def __init__(self):
        super(QWidget, self).__init__()

        main_layout = QVBoxLayout(self)

        self.buttons = []

        # Works:
        self.button_1 = QPushButton('Button 1 manual', self)
        main_layout.addWidget(self.button_1)
        self.buttons.append(self.button_1)
        self.button_1.clicked.connect(self.button_pushed)

        self.button_2 = QPushButton('Button 2 manual', self)
        main_layout.addWidget(self.button_2)
        self.buttons.append(self.button_2)
        self.button_2.clicked.connect(self.button_pushed)

        # Doesn't work:
        for idx in [3, 4]:
            button = QPushButton('Button {} auto'.format(idx), self)
            button.clicked.connect(self.button_pushed)
            self.buttons.append(button)
            main_layout.addWidget(button)


    def button_pushed(self):
        print('Pushed button {}'.format(self.buttons.index(self.sender())+1))


app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

QPushButton.clicked
信号发出一个参数,指示按钮的状态。当您连接到lambda插槽时,您分配给
idx
的可选参数将被按钮的状态覆盖

相反,将您的连接设置为

button.clicked.connect(lambda state, x=idx: self.button_pushed(x))

这样会忽略按钮状态,并将正确的值传递给方法。

小心!只要您将信号连接到一个带有self引用的lambda插槽,您的小部件就不会被垃圾收集!这是因为lambda创建了一个闭包,其中包含对小部件的另一个无法收集的引用


因此,
self.someUIwidget.someSignal.connect(lambda p:self.someMethod(p))
是非常邪恶的:)

(显然,创建所有按钮“自动”(在循环中)是很简单的,而不是两个在循环外,两个在循环内,如果这是你的最终目标的话…谢谢你的建议,但是上面的三个菠萝解决方案正是我想要的,并解释了发生了什么。是的,没有理由不在循环中生成所有四个按钮,除了显示一个示例of循环外的行为是如何不同的。我也很高兴了解您的操作方式有什么问题。我花了两天的时间对代码进行注释和取消注释,以发现这就是“漏洞”我正在调查的错误,然后我遇到了您的分析,这正是我最终得出的结论。现在开始更改所有现有代码……(为了记录,如果
lambda
self
传递给某个外部函数,也会出现相同的问题:
self.someUIwidget.someSignal.connect(lambda someNonSelfMethod(self))
那么什么是替代方案?@Esostack:取决于您想要实现的目标。如果您只想在插槽中检测发出信号的对象,您可以使用
QObject.sender()
方法。此外,您可以将信息存储在小部件属性中,并在插槽函数中访问它们,请参见此示例:@Rhdr ano值得一提的是这里的讨论是“状态”一词的真正用途?@ioaniatr当您连接到单击的
信号时,该信号具有所描述的签名。如您所见,当发出信号时,将提供一个包含按钮状态的参数(无论是否选中按钮)。可以根据需要调用代码中的变量,但该变量必须存在,以便Qt不会用选中状态覆盖下一个参数(
x=idx
)。