PyQt: 将信号合并到一个槽

2 投票
3 回答
3562 浏览
提问于 2025-04-15 12:35

我正在尝试减少在上下文菜单中使用的信号数量。这个菜单包含了一些操作,这些操作可以切换程序的工作模式,所以这些操作的实现其实很简单。根据QMenu::triggered的文档,

通常,你会把每个菜单项的triggered()信号连接到它自己特定的槽函数,但有时候你可能想把几个动作连接到同一个槽,比如当你有一组紧密相关的动作,比如“左对齐”、“居中”、“右对齐”。

不过,我不知道该怎么做,文档里也没有更详细的说明。
假设我在菜单actionMenu中有两个动作actionOpMode1actionOpMode2,还有一个槽函数setOpMode。我希望在调用setOpMode时,能够传入一个参数,这个参数能表明是哪个动作被触发了。我尝试了各种不同的方式来实现这个目标:

    QObject.connect(self.actionMenu, SIGNAL('triggered(QAction)'), self.setOpMode)  

但我甚至没有成功调用setOpMode,这说明actionMenu似乎从来没有“被触发”,可以这么说。

这个SO问题中,有人建议可以用lambda表达式来实现,但这样:

    QObject.connect(self.actionOpMode1, SIGNAL('triggered()'), lambda t: self.setOpMode(t))

却给出了"<lambda> () takes exactly 1 argument (0 given)"的错误提示。我不能说我完全理解这个是怎么工作的,所以在从clicked()切换到triggered()时,我可能做错了什么。

那该怎么做呢?

3 个回答

1

你可以使用 QObject::sender() 来找出哪个 QAction 发出了信号。

所以你的槽函数可能看起来像这样:

def triggered(self):
    sender = QtCore.QObject.sender()

    if sender == self.actionOpMode1:
        # do something
    elif sender == self.actionOpMode2:
        # do something else

关于你提到的另一个 StackOverflow 问题,里面用到了 lambda,简单来说,它创建了一个带有默认值的参数的 lambda。如果要把它应用到你的例子中,你需要这样做:

self.connect(self.actionOpMode1, QtCore.SIGNAL('triggered()'), lambda who="mode1": self.changeMode(who))
self.connect(self.actionOpMode2, QtCore.SIGNAL('triggered()'), lambda who="mode2": self.changeMode(who))

然后你可以有一个这样的成员函数:

def changeMode(self, who):
    if who == "mode1":
        # ...
    elif who == "mode2":
        # ...

我个人觉得第一种方法看起来更简洁、更易读。

2

我使用这种方法:

from functools import partial

def bind(self, action, *params):
    self.connect(action, QtCore.SIGNAL('triggered()'), 
                 partial(action, *params, self.onMenuAction))

def onMenuAction(self, *args):
    pass


bind(self.actionOpMode1, 'action1')
bind(self.actionOpMode2, 'action2')
4

使用QObject.Sender是一个解决办法,虽然不是最干净的方式。

可以使用QSignalMapper,这样可以更清晰地将一个值和发出信号的对象关联起来。

撰写回答