When to call asynchronous QMenu::popup() vs synchronous QMenu::exec()?

Viewed 861

Reading the Qt5 source code, I noticed that QLabel::contextMenuEvent() uses this (non-blocking) style:

QMenu *menu = ...
menu->setAttribute(Qt::WA_DeleteOnClose);
// Non-blocking
menu->popup(event->globalPos());

Alternatively, I have seen this (blocking) style from Qt code samples:

QMenu *menu = ...
// Blocking
QAction* action = menu->exec(event->globalPos());
// Or before menu->exec() call: menu->setAttribute(Qt::WA_DeleteOnClose)
delete menu;

I can see these differences:

  • Non-blocking vs blocking
  • Return void vs QAction*

Are their other differences? Example: Is there an advantage to non-blocking, e.g., the event loop can process other events? If the difference is purely style, please let me know.

Finally, I do notice that debugging the non-blocking style is a little bit weird for me on GNU/Linux/KDE, but that might be unrelated.

2 Answers

When to call QMenu::popup() vs QMenu::exec()?

The former: Always. The latter: Never. It's that easy.

Why? Because re-entering event loops leads to spaghetti code. The world as asynchronous and you can't pretend that you're "waiting" for something in synchronous-looking code while the world goes on and does all sorts of things that this code style hides from you. The exec() call really means "run arbitrary parts of my application, until an indeterminate time when the user decides they have had enough of the popup". If it sounds nasty, then it's meant to: it's nasty, buggy, terrible way to develop UI code, and leads to bugs that are hard to debug, and allows you to procrastinate instead of spending time actually doing it in the asynchronous fashion. I am appalled that the Qt Project still provides this in their code examples. exec() is bannable. There are very few cases where it has to be used due to broken Qt's platform-specific code. E.g. Qt didn't bother (last time I checked) to implement QDrag support from the main runloop on MacOS, so they spin up a local one just because Apple's example code shows the same idiocy, whereas it can be done without AFAIK (although it's nbot as trivial - but so what, it's library code, it's not supposed to be always trivial - otherwise the users could do it themselves).

You can use a state machine (QStateMachine) to specify the behavior of your application, having a dedicated state for when the particular popup is visible, and then you can do the necessary responses as the application exits that state. You can also write Qt ui code using C++20 coroutines, with some scaffolding code to allow that (Qt doesn't provide any yet).

I could add the following to your observations:

Taking look at the source code of QMenu::exec, it becomes clear, that this method is, so to say, superior to QMenu::popup, meaning that it extends its functionality, namely by adding an event loop:

QAction *QMenu::exec(const QPoint &p, QAction *action)
{
    ...
    QEventLoop eventLoop;
    d->eventLoop = &eventLoop;
    popup(p, action);
    ...
    (void) eventLoop.exec();
    ...
    d->eventLoop = nullptr;
    return action;
}

Effectively this makes handling an exec-uted QMenu more like a QDialog (meaning that it is modal and you get the result directly as a returned value), while a popup-ed one, more like a regular QWidget, with the result obtained through signals and slots. You might of course call this a matter of programming style, but depending on the application, the more low-level access of QMenu::popup compared to QMenu::exec might be a better fit.

Related