何时调用异步 QMenu::popup() 与同步 QMenu::exec()?
When to call asynchronous QMenu::popup() vs synchronous QMenu::exec()?
Reading the Qt5 source code,我注意到 QLabel::contextMenuEvent()
使用这种(非阻塞)样式:
QMenu *menu = ...
menu->setAttribute(Qt::WA_DeleteOnClose);
// Non-blocking
menu->popup(event->globalPos());
或者,我从 Qt 代码示例中看到了这种(阻塞)样式:
QMenu *menu = ...
// Blocking
QAction* action = menu->exec(event->globalPos());
// Or before menu->exec() call: menu->setAttribute(Qt::WA_DeleteOnClose)
delete menu;
我可以看到这些差异:
- 非阻塞与阻塞
- Return
void
对比 QAction*
他们还有其他区别吗?示例:非阻塞是否有优势,例如,事件循环可以处理其他事件?如果纯粹是风格的不同,请告诉我。
最后,我确实注意到在 GNU/Linux/KDE 上调试非阻塞样式对我来说有点奇怪,但这可能无关紧要。
我可以将以下内容添加到您的观察中:
查看 QMenu::exec
的源代码,很明显,可以说,此方法优于 QMenu::popup
,这意味着它扩展了其功能,即通过添加事件循环:
QAction *QMenu::exec(const QPoint &p, QAction *action)
{
...
QEventLoop eventLoop;
d->eventLoop = &eventLoop;
popup(p, action);
...
(void) eventLoop.exec();
...
d->eventLoop = nullptr;
return action;
}
实际上,这使得处理 exec
-uted QMenu
更像 QDialog
(这意味着它是模态的,您可以直接将结果作为返回值),而popup
-ed 一个,更像是一个常规的 QWidget
,结果是通过信号和槽获得的。您当然可以将其称为编程风格问题,但根据应用程序,与 QMenu::exec
相比,QMenu::popup
的 low-level 访问权限可能更合适。
When to call QMenu::popup() vs QMenu::exec()?
前者:总是。后者:从不。就是这么简单。
为什么?因为 re-entering 事件循环导致意大利面条代码。世界是异步的,你不能假装你在“等待”synchronous-looking 代码中的某些东西,而世界在继续并做这种代码风格对你隐藏的各种事情。 exec()
调用实际上意味着“运行 我的应用程序的任意部分,直到用户决定他们已经受够了弹出窗口的不确定时间”。如果这听起来很讨厌,那么它的意思是:它是开发 UI 代码的讨厌的、错误的、糟糕的方式,并且会导致难以调试的错误,并且允许您拖延而不是花时间在异步时尚。我很震惊 Qt 项目仍然在他们的代码示例中提供这个。 exec()
是可禁止的。由于 Qt 的 platform-specific 代码损坏而不得不使用它的情况非常少。例如。 Qt 没有(上次我检查过)在 MacOS 上实现来自主 运行 循环的 QDrag
支持,所以他们启动了一个本地循环,只是因为 Apple 的示例代码显示了同样的愚蠢,而它可以在没有 AFAIK 的情况下完成(尽管 nbot 是微不足道的——但那又怎样,它是库代码,它不应该总是微不足道的——否则用户可以自己做)。
您可以使用状态机 (QStateMachine
) 来指定应用程序的行为,在特定弹出窗口可见时具有专用状态,然后您可以在应用程序退出时做出必要的响应那个状态。您还可以使用 C++20 协程编写 Qt ui 代码,并使用一些脚手架代码来实现这一点(Qt 尚未提供)。
Reading the Qt5 source code,我注意到 QLabel::contextMenuEvent()
使用这种(非阻塞)样式:
QMenu *menu = ...
menu->setAttribute(Qt::WA_DeleteOnClose);
// Non-blocking
menu->popup(event->globalPos());
或者,我从 Qt 代码示例中看到了这种(阻塞)样式:
QMenu *menu = ...
// Blocking
QAction* action = menu->exec(event->globalPos());
// Or before menu->exec() call: menu->setAttribute(Qt::WA_DeleteOnClose)
delete menu;
我可以看到这些差异:
- 非阻塞与阻塞
- Return
void
对比QAction*
他们还有其他区别吗?示例:非阻塞是否有优势,例如,事件循环可以处理其他事件?如果纯粹是风格的不同,请告诉我。
最后,我确实注意到在 GNU/Linux/KDE 上调试非阻塞样式对我来说有点奇怪,但这可能无关紧要。
我可以将以下内容添加到您的观察中:
查看 QMenu::exec
的源代码,很明显,可以说,此方法优于 QMenu::popup
,这意味着它扩展了其功能,即通过添加事件循环:
QAction *QMenu::exec(const QPoint &p, QAction *action)
{
...
QEventLoop eventLoop;
d->eventLoop = &eventLoop;
popup(p, action);
...
(void) eventLoop.exec();
...
d->eventLoop = nullptr;
return action;
}
实际上,这使得处理 exec
-uted QMenu
更像 QDialog
(这意味着它是模态的,您可以直接将结果作为返回值),而popup
-ed 一个,更像是一个常规的 QWidget
,结果是通过信号和槽获得的。您当然可以将其称为编程风格问题,但根据应用程序,与 QMenu::exec
相比,QMenu::popup
的 low-level 访问权限可能更合适。
When to call QMenu::popup() vs QMenu::exec()?
前者:总是。后者:从不。就是这么简单。
为什么?因为 re-entering 事件循环导致意大利面条代码。世界是异步的,你不能假装你在“等待”synchronous-looking 代码中的某些东西,而世界在继续并做这种代码风格对你隐藏的各种事情。 exec()
调用实际上意味着“运行 我的应用程序的任意部分,直到用户决定他们已经受够了弹出窗口的不确定时间”。如果这听起来很讨厌,那么它的意思是:它是开发 UI 代码的讨厌的、错误的、糟糕的方式,并且会导致难以调试的错误,并且允许您拖延而不是花时间在异步时尚。我很震惊 Qt 项目仍然在他们的代码示例中提供这个。 exec()
是可禁止的。由于 Qt 的 platform-specific 代码损坏而不得不使用它的情况非常少。例如。 Qt 没有(上次我检查过)在 MacOS 上实现来自主 运行 循环的 QDrag
支持,所以他们启动了一个本地循环,只是因为 Apple 的示例代码显示了同样的愚蠢,而它可以在没有 AFAIK 的情况下完成(尽管 nbot 是微不足道的——但那又怎样,它是库代码,它不应该总是微不足道的——否则用户可以自己做)。
您可以使用状态机 (QStateMachine
) 来指定应用程序的行为,在特定弹出窗口可见时具有专用状态,然后您可以在应用程序退出时做出必要的响应那个状态。您还可以使用 C++20 协程编写 Qt ui 代码,并使用一些脚手架代码来实现这一点(Qt 尚未提供)。