2009-06-30 3 views
2

Я пытаюсь уменьшить количество сигналов, которые я должен использовать в своих контекстных меню. Меню состоит из действий, которые переключают режим работы программы, поэтому операция, выполняемая слотами, очень проста. Цитирование документацию по QMenu :: срабатывает,PyQt: Объединение сигналов в один слот

нормально, при подключении срабатывает сигнал каждого меню действий (в) его собственный пользовательский слот, но иногда вы хотите подключить несколько действий к одному слоту, например, когда у вас есть группа тесно связанных действий, таких как «left justify», «center», «right justify».

Однако я не могу понять, как это осуществить, и документация не вдавалась в подробности.
Предположим, у меня есть действия actionOpMode1 и actionOpMode2 в меню actionMenu и слот setOpMode. Я хочу, чтобы setOpMode вызывался с параметром, который каким-то образом связан с тем, какое действие было инициировано. Я пробовал различные перестановки на эту тему:

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

Но я даже не получил его называть setOpMode, что говорит о том, что actionMenu никогда не «чувствует себя срабатывает», так сказать.

В this SO question, он предположил, что это может быть сделано с lamdbas, но это:

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

дает "<lambda>() takes exactly 1 argument (0 given)". Я не могу сказать, что я действительно понимаю, как это должно работать, поэтому я, возможно, сделал что-то неправильно при переходе с нажатого() на triggered().

Как это сделать?

ответ

4

Использование QObject.Sender является одним из раствора, хотя и не самый чистый один.

Используйте QSignalMapper, чтобы связать чистое значение с объектом, который излучал сигнал.

+0

+1, QSignalMapper спроектирован _exactly_ для этой цели (и довольно элегантно, я тоже думаю), поэтому он предлагает архитектурное решение. –

+0

-1 Не используйте QSignalMapper в Python, нет необходимости. –

1

Вы можете использовать QObject::sender(), чтобы выяснить, какой QAction излучал сигнал.

Так что ваш слот может выглядеть следующим образом:

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

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

Что касается того, что происходит в другой SO вопрос вы упомянули с лямбда, что он делает, это создать лямбда с одним параметром, который имеет значение по умолчанию, так чтобы применить это к вашему примеру вам нужно сделать что-то вроде этого:

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": 
     # ... 

Лично первый подход выглядит более чистым и более читаемым для меня.

+1

connect() не увеличивает счетчик ресивера; вы не можете использовать lambda в соединениях, если вы не держите дополнительную ссылку. –

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') 
Смежные вопросы