One method to handle all menu items?
-
Currently the way that I'm aware to set up a menu callback is to connect it to a method e.g.
connect(action1, &QAction::triggered, this, &onMenuA); connect(action2, &QAction::triggered, this, &onMenuB);
However I'm wondering, rather than create an onMenu callback for each and every menu item,
how can I have one callback that handles all of them?
i.e. how can I identify in my callback which menu item it was that was chosen by the user?
Thanks. -
To answer my own question, a slot method can call sender() to learn who "sent" the message (in OO parlance) i.e. who called the slot method.
Using that you can get the QAction that was triggered:
QAction *actionThatUserChose = qobject_cast<QAction *>(sender());
Voila.
-
@Publicnamer
You can achieve it this way, but there are two alternatives.This class collects a set of parameterless signals, and re-emits them with integer, string or widget parameters corresponding to the object that sent the signal. Note that in most cases you can use lambdas for passing custom parameters to slots. This is less costly and will simplify the code.
As mentioned there, instead of using this or your
sender()
approach which is frowned upon, the "modern" way of doing what you want is to use C++ lambdas for your connections, passing an additional parameter of the sender into the slot method:connect(action1, &QAction::triggered, this, [this, action1](bool checked = false) { onMenu(action1); }); connect(action2, &QAction::triggered, this, [this, action2](bool checked = false) { onMenu(action2); }); // slot definition void Class::onMenu(const QAction &action) { }
Once you start using lambdas you will find you can do a lot more powerful/flexible things than via sender or signal mapper approaches.
-
@mpergand
I didn't know about this. I see it says:Normally, you connect each menu action to a single slot using QAction::triggered(), but sometimes you will want to connect several items to a single slot (most often if the user selects from an array). This signal is useful in such cases.
and I see it passes the triggering
QAction
as a parameter to the slot. This is good. Whether the OP uses this or the lambda approach those are the best way to get at the trigger. -
@JonB To go a little bit further.
When you use lambda, it's useless to call another function like onMenu(), because you lose the kind of action is it !
Why not doing this:action=menu->addAction(tr("Copy")); action->setShortcut(QKeySequence::Copy); connect(action, &QAction::triggered, [this, action]() { // do copy here});
-
@mpergand
I named itonMenu()
purely because that is what the OP was using in his code.Of course you could do the specific action in each lambda. Just as the OP could have written separate slots for each
QAction
. But that was not his question, which (as per the title) is "One method to handle all menu items?". He perhaps has many related items which he wants to channel through some shared code. -
@JonB said in One method to handle all menu items?:
connect(action1, &QAction::triggered, this, [this, action1](bool checked = false) { onMenu(action1); });
Thanks, that helps.
I do need the onMenu call as it happens, because there is a lot that I need to do in response to the menu invocation and putting it all in a lambda would be unseemly. Just curious what is the checked parameter for? I omitted it and things work fine. -
@Publicnamer said in One method to handle all menu items?:
putting it all in a lambda would be unseemly
Absolutely! You can write a multiline lambda as big as you like, but I keep them to one line, calling some other method to do big stuff but with whatever extra parameters I need from any variables where the
connect()
appears.Just curious what is the checked parameter for? I omitted it and things work fine.
You obviously have never noticed, but the
QAction::triggered
signal actually passes this: void QAction::triggered(bool checked = false):If the action is checkable, checked is true if the action is checked, or false if the action is unchecked.
I only put it in in case you were using it, to match the original signal signature. Here I could have omitted it, and I received the parameter in my lambda but didn't actually bother to pass it onto
onMenu()
. I too would probably have left it out here.