PyQt4:对于“按钮列表”,信号槽机制无法正常工作

2024-09-29 07:24:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要为我的申请在窗口上放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_())

''''''

我使用标准的signal/slot机制将每个按钮连接到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)函数可以正常工作,但它效率很低,而且很麻烦。有人知道第一个(更干净的)实现出了什么问题吗?你知道吗

注意:我在windows10上工作,使用pythonv3并使用PyQt4库(anaconda包)制作GUI。你知道吗

编辑

谢谢你,所罗门多西先生。现在你的解决方案成功了!我首先要导入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])

        ''''''

    ''''''

Tags: lambdaimportselfisdefconnect按钮frame
1条回答
网友
1楼 · 发布于 2024-09-29 07:24:57

您需要为每个创建的(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))

相关问题 更多 >