PyQt4:信号插槽机制无法正常用于“按钮列表”

时间:2016-05-19 10:02:58

标签: python qt lambda pyqt pyqt4

我需要在窗口上放置6个按钮以供我的应用程序使用。但随着应用程序的增长,可能需要更多按钮(可能是7,8,......)。这就是我将这些按钮放在Python列表中的原因。您可以在下面看到我的代码。您可以简单地复制/粘贴它,它应该在您的系统上运行而不会出现任何错误:

enter image description here

import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore

def setCustomSize(x, width, height):
    sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
    sizePolicy.setHorizontalStretch(0)
    sizePolicy.setVerticalStretch(0)
    sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
    x.setSizePolicy(sizePolicy)
    x.setMinimumSize(QtCore.QSize(width, height))
    x.setMaximumSize(QtCore.QSize(width, height))

''''''

class CustomMainWindow(QtGui.QMainWindow):

    def __init__(self):

        super(CustomMainWindow, self).__init__()

        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")

        # Create FRAME_A
        self.FRAME_A = QtGui.QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
        self.LAYOUT_A = QtGui.QHBoxLayout()
        self.LAYOUT_A.setAlignment(QtCore.Qt.AlignLeft)
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)

        # Place the buttons
        self.placeButtons()

        self.show()

    ''''''

    def placeButtons(self):
        self.btn = []

        for i in range(6):
            self.btn.append(QtGui.QPushButton(text = 'btn[' + str(i) + ']'))
            setCustomSize(self.btn[i], 100, 50)
            self.btn[i].clicked.connect(lambda: self.btnAction(i))            
            self.LAYOUT_A.addWidget(self.btn[i])

        ''''''

    ''''''


    def btnAction(self,n):
        print("btn[" + str(n) + "] is clicked")

    ''''''



''' End Class '''



if __name__== '__main__':
    app = QtGui.QApplication(sys.argv)
    QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()


    sys.exit(app.exec_())

''''''

我使用标准信号/插槽机制将每个按钮连接到btnAction(self,n)功能。如您所见,我使用了一个特殊功能 - 让我们称之为lambda功能 - 传递一个参数。参数是按钮编号。

不幸的是,无论我按下什么按钮,我都会得到以下输出:

>>> btn[5] is clicked
>>> btn[5] is clicked
>>> btn[5] is clicked
>>> btn[5] is clicked

我将placeButtons(self)功能更改为:

    def placeButtons(self):
        self.btn = []

        for i in range(6):
            self.btn.append(QtGui.QPushButton(text = 'btn[' + str(i) + ']'))
            setCustomSize(self.btn[i], 100, 50)
            if(i==0):
                self.btn[i].clicked.connect(lambda: self.btnAction(0))
            elif(i==1):
                self.btn[i].clicked.connect(lambda: self.btnAction(1))
            elif(i==2):
                self.btn[i].clicked.connect(lambda: self.btnAction(2))
            elif(i==3):
                self.btn[i].clicked.connect(lambda: self.btnAction(3))
            elif(i==4):
                self.btn[i].clicked.connect(lambda: self.btnAction(4))
            elif(i==5):
                self.btn[i].clicked.connect(lambda: self.btnAction(5))

            self.LAYOUT_A.addWidget(self.btn[i])

        ''''''

    ''''''

现在我得到了正确的输出:

>>> btn[0] is clicked
>>> btn[1] is clicked
>>> btn[2] is clicked
>>> btn[3] is clicked

所以新的placeButtons(self)函数有效,但它效率低且繁琐。有谁知道第一个(和更清洁的)实现有什么问题?

注意:我在Windows 10上工作,使用Python v3并使用PyQt4库(anaconda包)制作我的GUI。

修改

谢谢先生。 salomonderossi。现在你的解决方案有效我首先要导入functools:

import functools

接下来,我将应用您的解决方案:

    def placeButtons(self):
        self.btn = []

        for i in range(6):
            self.btn.append(QtGui.QPushButton(text = 'btn[' + str(i) + ']'))
            setCustomSize(self.btn[i], 100, 50)
            self.btn[i].clicked.connect(functools.partial(self.btnAction, i))
            self.LAYOUT_A.addWidget(self.btn[i])

        ''''''

    ''''''

1 个答案:

答案 0 :(得分:2)

您需要为每个创建的(lambda)函数绑定值。为此,您可以将它们作为参数传递,并使用默认值:

for i in range(6):
    self.btn[i].clicked.connect(lambda i=i: self.btnAction(i))

以下是显示效果的最小示例:

def test_function(x):
    return x


def test():
    # DO NOT USE THIS. This is just for showing the effect
    wrong = list()
    for i in range(6):
        wrong.append(lambda: test_function(i))

    for w in wrong:
        print(w())  # will always print 5

    print("-"*10)
    # This is the desired behaviour
    right = list()
    for i in range(6):
        right.append(lambda i=i: test_function(i))

    for r in right:
        print(r())

if __name__ == '__main__':
    test()

其中给出了以下输出:

5
5
5
5
5
5
----------
0
1
2
3
4
5

或者,您可以使用functools包中的partial

for i in range(6):
    self.btn[i].clicked.connect(partial(self.btnAction, i))