QAction 表示一项可以从菜单、工具栏或快捷键触发的命令。同一个 action 加到多个入口时,共享可用、选中和文本等状态,不必为每个入口分别维护一套开关。
本文 C++ 示例的 --self-test 使用 assert,应在 Debug 构建或未定义 NDEBUG 的情况下运行;Release 构建若禁用了断言,退出成功不代表这些检查已经执行。
三个状态与两个常用信号
跳转到“三个状态与两个常用信号”| 状态或信号 | 含义 | 常用写法 |
|---|---|---|
| checkable | 动作是否具有开/关状态;不保证每个平台都画成复选框 | action->setCheckable(true) |
| checked | 当前是否选中,先启用 checkable | action->setChecked(true) |
| enabled | 用户是否可以操作该动作 | action->setEnabled(false) |
toggled(bool) | 选中状态发生变化,包括程序调用 setChecked | 同步实际开关状态 |
triggered(bool) | 动作被触发;单独 setChecked 不会产生此信号 | 执行命令,或处理用户触发 |
这些信号的用途有交集,但不能因为“都带 bool”就接错。初始化时如果先设 checked、再连接 toggled,连接建立前那次变化不会补发,应直接初始化相应界面。QAction
完整菜单与共享工具栏示例
跳转到“完整菜单与共享工具栏示例”下面合并原来的菜单片段和独立主窗口程序,保留 action1/action2 与 Toggle Action 的用途。保存为 main.cpp,使用 Qt 6 Widgets、C++17 构建,传入 --self-test 检查信号和对象关系。
#include <QApplication>#include <QAction>#include <QLabel>#include <QMainWindow>#include <QMenu>#include <QMenuBar>#include <QStyle>#include <QToolBar>#include <cassert>
int main(int argc, char *argv[]) { QApplication app(argc, argv); QMainWindow window; auto *status = new QLabel("Toggle is ON", &window); window.setCentralWidget(status); auto *bar = window.menuBar(); auto *regular = new QMenu("Menu: regular", &window); auto *action1 = new QAction(window.style()->standardIcon(QStyle::SP_ArrowRight), "action1", &window); auto *action2 = new QAction("action2", &window); regular->addActions({action1, action2}); bar->addMenu(regular); regular->addSeparator(); auto *toggle = new QAction("Toggle Action", &window); toggle->setCheckable(true); toggle->setChecked(true); toggle->setEnabled(true); bar->addMenu("Options")->addAction(toggle); auto *toolbar = window.addToolBar("Commands"); toolbar->addAction(toggle); int stateChanges = 0; int activations = 0; QObject::connect(toggle, &QAction::toggled, &window, [&](bool checked) { ++stateChanges; status->setText(checked ? "Toggle is ON" : "Toggle is OFF"); }); QObject::connect(toggle, &QAction::triggered, &window, [&](bool) { ++activations; }); QObject::connect(action1, &QAction::triggered, &window, [status] { status->setText("action1 triggered"); }); QObject::connect(action2, &QAction::triggered, &window, [status] { status->setText("action2 triggered"); }); if (app.arguments().contains("--self-test")) { assert(toggle->isCheckable() && toggle->isChecked()); assert(action1->parent() == &window); assert(regular->actions().contains(action1)); assert(toolbar->actions().contains(toggle)); toggle->setChecked(false); assert(stateChanges == 1 && activations == 0); assert(status->text() == "Toggle is OFF"); toggle->trigger(); assert(toggle->isChecked() && stateChanges == 2 && activations == 1); action1->trigger(); assert(status->text() == "action1 triggered"); toggle->setEnabled(false); assert(!toggle->isEnabled()); return 0; } window.resize(460, 220); window.show(); return app.exec();}原代码中的 QMenuBar::addMenu(QMenu *) 并没有在 Qt 6 被整体弃用;它仍是可用重载。此重载不会取得传入 QMenu 的所有权,所以例子给 menu 和 actions 都指定主窗口为父对象。向菜单添加一个现成 QAction 也不应被当成自动修改它的 QObject 父对象。QMenuBar::addMenu、QWidget::addAction
原片段把创建菜单放在按钮点击槽中,反复点击会反复新建菜单栏;固定菜单应在窗口初始化时创建一次。若确实需要动态菜单,就明确更新、复用或销毁旧对象。原图标路径 :/resource/image/向右箭头.jpg 需要对应资源文件;本例使用标准图标,让独立程序不依赖未给出的 qrc。
原交互记录
跳转到“原交互记录”
动画记录的是展开菜单、移动高亮和关闭菜单的过程,不能仅从悬停高亮判定 action 的 checked 状态。实际选中指示、图标和菜单位置受平台样式影响。菜单内部的分隔线使用 QMenu::addSeparator();QMenuBar::addSeparator() 是菜单栏上的分隔 action,平台可能以不同方式呈现,不保证是一根竖线。