38

I have a taskbar menu that when clicked is connected to a slot that gets the trigger event. Now the problem is that I want to know which menu item was clicked, but I don't know how to send that information to the function connected to. Here is the used to connect the action to the function:

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

I know that some events return a value, but triggered() doesn't. So how do I make this happen? Do I have to make my own signal?

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
johannix
  • 29,188
  • 15
  • 39
  • 42

5 Answers5

54

Use a lambda

Here's an example from the PyQt book:

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

By the way, you can also use functools.partial, but I find the lambda method simpler and clearer.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
21

As already mentioned here you can use the lambda function to pass extra arguments to the method you want to execute.

In this example you can pass a string obj to the function AddControl() invoked when the button is pressed.

# 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

Source: snip2code - Using Lambda Function To Pass Extra Argument in PyQt4

Community
  • 1
  • 1
Dominique Terrs
  • 609
  • 8
  • 5
  • 1
    This should be the accepted answer as the previously accepted answer method is now outdated. – NL23codes Jan 25 '20 at 00:16
  • 1
    if you are going to pass variables with lambda you should know that lambda will cause a memory leak, if the signal is not properly disconnected (https://stackoverflow.com/q/61871629/5148062) It may be negligible when passing small variables but passing list around could kill your app – Rhdr Jul 13 '20 at 09:02
5

I'd also like to add that you can use the sender method if you just need to find out what widget sent the signal. For example:

def menuClickedFunc(self):
    # The sender object:
    sender = self.sender()
    # The sender object's name:
    senderName = sender.objectName()
    print senderName
shalinar
  • 51
  • 1
  • 1
4

use functools.partial

otherwise you will find you cannot pass arguments dynamically when script is running, if you use lambda.

Eric Wang
  • 1,009
  • 1
  • 9
  • 16
  • 2
    Care to back this up with some evidence? – ekhumoro Dec 01 '15 at 16:05
  • You very well can pass arguments dynamically into a created lambda, as lambdas (like normal `def`'ed functions) are closures and thus capture the bindings from their enclosing scope. (Yes, that also implies that as a third option you could use a locally `def`'ed function _and_ dynamically pass parameters to it.) Also, don't forget that you can use default arguments to pass dynamic values to _both_ of them. However, having said that, personally I prefer `functools.partial` as well, my own reason being that it "feels" more lightweight than a `lambda` (without making a claim that it actually is). – blubberdiblub Jan 11 '17 at 23:19
0

In general, you should have each menu item connected to a different slot, and have each slot handle the functionality only for it's own menu item. For example, if you have menu items like "save", "close", "open", you ought to make a separate slot for each, not try to have a single slot with a case statement in it.

If you don't want to do it that way, you could use the QObject::sender() function to get a pointer to the sender (ie: the object that emitted the signal). I'd like to hear a bit more about what you're trying to accomplish, though.

KeyserSoze
  • 2,501
  • 1
  • 16
  • 17
  • 5
    The OP is asking about a perfectly legitimate need. Sometimes many menu items are auto-generated (or just very similar) and should refer to the same slot. There is a standard way to achieve this in PyQt which is recommended in the book and in tutorials. – Eli Bendersky Jun 02 '09 at 16:56