PyQt 在连接到信号时将参数发送到插槽

问题描述 投票:0回答:6

我有一个任务栏菜单,单击该菜单时会连接到获取触发事件的插槽。现在的问题是我想知道单击了哪个菜单项,但我不知道如何将该信息发送到连接的函数。这是用于将操作连接到函数的:

QtCore.QObject.connect(menuAction, 'triggered()', menuClickedFunc)

我知道有些事件会返回一个值,但triggered()不会。那么我该如何实现这一点呢?我必须自己发出信号吗?

python qt4 pyqt
6个回答
53
投票

使用

lambda

这是 PyQt 书中的示例

self.connect(button3, SIGNAL("clicked()"),
    lambda who="Three": self.anyButton(who))

顺便说一句,你也可以使用

functools.partial
,但我发现
lambda
的方法更简单、更清晰。


21
投票

正如已经提到的这里,您可以使用 lambda 函数将额外的参数传递给您想要执行的方法。

在此示例中,您可以将字符串 obj 传递给按下按钮时调用的函数 AddControl()。

# Create the build button with its caption
self.build_button = QPushButton('&Build Greeting', self)
# Connect the button's clicked signal to AddControl
self.build_button.clicked.connect(lambda: self.AddControl('fooData'))
def AddControl(self, name):
    print name

来源:snip2code - 使用 Lambda 函数在 PyQt4 中传递额外参数


5
投票

我还想补充一点,如果您只需要找出哪个小部件发送了信号,您可以使用

sender
方法。例如:

def menuClickedFunc(self):
    # The sender object:
    sender = self.sender()
    # The sender object's name:
    senderName = sender.objectName()
    print senderName

3
投票

使用 functools.partial

否则,如果你使用 lambda,你会发现在脚本运行时无法动态传递参数。


0
投票

一般来说,您应该将每个菜单项连接到不同的插槽,并且让每个插槽仅处理其自己的菜单项的功能。例如,如果您有“保存”、“关闭”、“打开”等菜单项,则应该为每个菜单项创建一个单独的插槽,而不是尝试在其中包含一个带有 case 语句的插槽。

如果您不想这样做,您可以使用 QObject::sender() 函数来获取指向发送者的指针(即:发出信号的对象)。不过,我想更多地了解您想要实现的目标。


0
投票

其他答案中建议的方法有问题,

self.whatever.connect(lambda x: self.method(..., x))        # approach 1 (suboptimal)
self.whatever.connect(functools.partial(self.method, ...))  # approach 2 (suboptimal)

它们创建了一个引用循环:

self
对象持有对(或者是)带有信号的对象的引用,该对象持有对函数或
partial
对象的引用,该对象持有对
self的引用
对象。结果是(在 CPython 中)当所有其他对这些对象的引用消失时,这些对象都不会被垃圾回收;它们只会在下次循环收集器运行时被收集。反过来,他们将保留他们引用的所有其他 Python 数据结构以及他们共同拥有的任何 Qt 对象。这并不完全是内存泄漏,因为所有内容最终都会被释放,但这可能是一个问题。

这样写就没有引用循环

self.whatever.connect(self.method)

因为在 PyQt 和 PySide 中,

connect
对于 Python 绑定方法对象有一个特殊情况:它不是保存对绑定方法的引用,而是提取其两个字段(
__self__
__func__
)并保存弱引用到
__self__
以及对
__func__
的普通引用。如果
__self__
消失,连接将自动断开。

您可以通过编写以下内容来利用内联

lambda
函数的行为:

self.whatever.connect((lambda obj, x: obj.method(..., x)).__get__(self))  # approach 1' (better)

__get__
是创建绑定方法对象的函数对象的方法。

您可以通过编写

functools.partial
的替代品来让这变得不那么尴尬,它返回正确的魔法类型的对象:

def partial_bound_method(bound_method, *args, **kwargs):
    f = functools.partialmethod(bound_method.__func__, *args, **kwargs)
    # NB: the seemingly redundant lambda is needed to ensure the correct result type
    return (lambda *args: f(*args)).__get__(bound_method.__self__)

...

self.whatever.connect(partial_bound_method(self.method, ...))  # approach 2' (better)

这是一个测试,它是否按预期工作:

# replace PyQt5 with PyQt6/PySide2/PySide6 as appropriate
from PyQt5.QtCore import QObject, QCoreApplication
import functools, weakref

def partial_bound_method(bound_method, *args, **kwargs):
    f = functools.partialmethod(bound_method.__func__, *args, **kwargs)
    # NB: the seemingly redundant lambda is needed to ensure the correct result type
    return (lambda *args: f(*args)).__get__(bound_method.__self__)

app = QCoreApplication([])

class Class(QObject):
    def method(*args): pass

def test(maketarget):
    obj = Class()
    # the signal doesn't matter; this is one that happens to exist in QObject
    obj.objectNameChanged.connect(maketarget(obj))
    obj = weakref.ref(obj)
    print('not freed' if obj() else 'freed')

test(lambda obj: obj.method)
test(lambda obj: lambda *args: obj.method('x', *args))
test(lambda obj: functools.partial(obj.method, 'x'))
test(lambda obj: partial_bound_method(obj.method, 'x'))

应该打印出来

freed
not freed
not freed
freed
© www.soinside.com 2019 - 2024. All rights reserved.