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

2024-12-30 08:41:00
admin
原创
40
摘要:问题描述:我正在尝试使用 lambda 函数连接插槽,但它没有按我预期的方式工作。在下面的代码中,我成功正确连接了前两个按钮。对于我循环连接的后两个按钮,这出错了。在我之前有人有同样的问题(Qt - 使用 lambda 连接带参数的插槽),但这个解决方案对我不起作用。我盯着屏幕看了半个小时,但我不知道我的代码...

问题描述:

我正在尝试使用 lambda 函数连接插槽,但它没有按我预期的方式工作。在下面的代码中,我成功正确连接了前两个按钮。对于我循环连接的后两个按钮,这出错了。在我之前有人有同样的问题(Qt - 使用 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”的结果,按下另外两个按钮都会产生“按下按钮 False”的结果,尽管我预期的是 3 和 4。

我还没有完全理解 lambda 机制。到底连接了什么?指向 lambda 生成的函数的指针(替换了参数)还是每当信号触发时都会评估 lambda 函数?


解决方案 1:

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

相反,请按照以下方式建立连接:

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

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

解决方案 2:

注意!只要您将信号连接到带有自身引用的 lambda 槽,您的小部件就不会被垃圾回收!这是因为 lambda 创建了一个闭包,其中包含对小部件的另一个不可回收的引用。

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

解决方案 3:

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

但我认为你不需要这样做。看起来你使用 lambda 的唯一原因是你可以将参数传递给 button_pushed(),以识别它是哪个按钮。在 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_())

解决方案 4:

这很简单。检查工作代码和不工作代码。您有语法错误。

工作代码:

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

不起作用:

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

使固定:

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

对于 lambda,您正在定义一个“x”函数,并将该函数解释为“self.button_pushed(idx)”,以便将参数与您的函数一起使用,在这种情况下,参数是 (idx)。只需尝试一下,然后让我知道它是否有效。

他遇到的问题是他试图通过 for 循环创建获得不同的输出。不幸的是,它将最后一个值分配给名为 button 的任何变量,因此结果为 4。前两个是有效的,因为它们不是在 for 循环中创建的,而是单独创建的。

按钮变量的名称在工作中是不同的,如 button_1 和 button_2。在 for 循环中创建的所有按钮都将具有名称 button,从而产生相同的功能。

他尝试做的解决方案如下,并且非常有效。

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

buttons = []

def newWin():
    window = QWidget()
    window.setWindowTitle("Lambda Loop")
    window.setFixedWidth(1000)
    window.move(175, 10)
    window.setStyleSheet("background: #161219;")
    grid = QGridLayout()
    return window, grid

def newButton(text :str, margin_left, margin_right, x):
    button = QPushButton(text)
    button.setCursor(QCursor(Qt.PointingHandCursor))
    button.setFixedWidth(485)
    button.setStyleSheet(
        "*{border: 4px solid '#BC006C';" +
        "margin-left: " + str(margin_left) + "px;" +
        "margin-right: " + str(margin_right) + "px;" +
        "color: 'white';" +
        "font-family: 'Comic Sans MS';" +
        "font-size: 16px;" +
        "border-radius: 25px;" +
        "padding: 15px 0px;" +
        "margin-top: 20px;}" +
        "*:hover {background: '#BC006C'}"
    )

    def pushed():
        val = x
        text = QLabel(str(val))
        text.setAlignment(Qt.AlignRight)
        text.setStyleSheet(
            "font-size: 35px;" +
            "color: 'white';" +
            "padding: 15px 15px 15px 25px;" +
            "margin: 50px;" +
            "background: '#64A314';" +
            "border: 1px solid '#64A314';" +
            "border-radius: 0px;"
        )
        grid.addWidget(text, 1, 0)
    button.clicked.connect(pushed)
    return button

app = QApplication(argv)
window, grid = newWin()

def frame1(grid):
    for each in [3, 4]:
        button = newButton('Button {}'.format(each), 150, 150, each)
        buttons.append(button)
        pass
    b_idx = 0
    for each in buttons:
        grid.addWidget(each, 0, b_idx, 1, 2)
        b_idx += 1

frame1(grid)

window.setLayout(grid)

window.show()

exit(app.exec())

我把所有内容都放在一个地方,这样所有人都能看到。说出你想要做什么比猜测更容易。(你也可以在 Frame 函数的列表中添加新变量,它会为你创建更多具有不同值和功能的按钮。)

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1000  
  华为作为全球领先的信息与通信技术(ICT)解决方案提供商,其全球化项目的成功离不开高效的项目管理方法。其中,集成产品开发(IPD)流程是华为项目管理体系的核心组成部分。IPD流程不仅帮助华为在复杂的全球化项目中实现了资源的高效整合,还通过跨部门协作和持续优化,确保了项目的高质量交付。本文将通过具体案例,分析华为IPD流...
IPD测试流程   0  
  IPD(Integrated Product Development)是一种以跨职能团队协作为核心的产品开发流程,旨在通过整合资源、优化流程和提高决策效率,实现产品从概念到市场的快速、高效交付。IPD流程的核心思想是将传统的串行开发模式转变为并行开发模式,通过跨部门协作和早期风险识别,减少开发周期中的浪费和返工。这种方...
IPD流程分为几个阶段   0  
  华为的集成产品开发(IPD)流程是企业项目管理中的经典实践,其核心在于通过跨部门协同实现高效的产品开发。IPD流程强调从市场需求到产品交付的全生命周期管理,而跨部门沟通则是这一流程成功的关键。在华为的实践中,跨部门沟通不仅仅是信息的传递,更是团队协作、目标对齐和资源整合的重要手段。本文将深入探讨IPD流程中的跨部门沟通...
IPD项目管理咨询   0  
  IPD流程全称是集成产品开发(Integrated Product Development),它是一种以客户需求为导向、跨部门协作的产品开发模式。与传统产品开发模式相比,IPD强调在产品开发的早期阶段就整合市场、研发、制造、采购等多个部门的资源和能力,通过并行工程和协同工作来提升开发效率。IPD流程的核心在于打破部门壁...
IPD产品开发流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用