How to iterate through a menu's actions in Qt?
我正在一个项目中工作,我需要自动打开(显示或弹出)QMenuBar 中的项目。
假设我有下一个菜单栏:
1 2 3 | File Edit Help -op1 -op1 -op1 -op2 -op2 -op2 |
要设置一个动作(显示与该动作相关的菜单),我使用:
1 | menuBar->setActiveAction(mymenuactionpointer); |
据我所知,我可以使用以下方法之一来获取指向 QMenuBar 元素的指针列表:
1 | QMenuBar::actions(); |
或
1 2 3 | QList<Object*> lst1 = QMenuBar::findChildren<QObject*>(); QList<Object*> lst2 = QMenuBar::findChildren<QAction*>(); |
当我使用
当我使用
1 2 3 | QAction *a = (QAction *)lst1.at(0); QAction *a = qobject_cast<QAction*>(lst1.at(0)); QAction *a = dynamic_cast<QAction*>(lst1.at(0)); |
在所有这些情况下,
我一直在搜索,我在这里发现,在获取菜单栏操作列表后,可以询问
1 | QList<Object*> lst2 = QMenuBar::findChildren<QAction*>(); |
每个元素 "File, Edit Help"
枚举
了解每个 QMenu 也与一个 QAction 相关联至关重要。所以知道了,正确的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void enumerateMenu(QMenu *menu) { foreach (QAction *action, menu->actions()) { if (action->isSeparator()) { qDebug("this action is a separator"); } else if (action->menu()) { qDebug("action: %s", qUtf8Printable(action->text())); qDebug(">>> this action is associated with a submenu, iterating it recursively..."); enumerateMenu(action->menu()); qDebug("<<< finished iterating the submenu"); } else { qDebug("action: %s", qUtf8Printable(action->text())); } } } |
下面是如何遍历菜单栏中的每个菜单项,它还会查找下面的任何菜单,因此这里不需要递归调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // assuming you have a private QActionGroup* actions; defined in the header.. // ...and a slot named 'onAction(QAction*)' as well... this should work: QList<QMenu*> lst; lst = ui->menuBar->findChildren<QMenu*>(); actions = new QActionGroup(this); foreach (QMenu* m, lst) { foreach (QAction* a, m->actions()) { actions->addAction(a); } } connect(actions,SIGNAL(triggered(QAction*)),this,SLOT(onAction(QAction*))); |
如您所见,然后您可以连接一个主插槽来处理一个动作可能引发的各种事件(我只是在这里展示了触发,但您明白了)。希望这会有所帮助.. 某人..
注意事项
我将 QActionGroup 用于使用您可能迭代的列表的示例目的,但除非您正在处理无线电组,否则您真的不应该使用它,因为这就是它的用途。其次,如果您想要这些操作,因为您计划将它们链接到一个方法来处理所有项目,我建议您使用 QMenu 的触发/悬停信号,或者如果您需要知道菜单何时即将弹出,您\\'将需要 QMenuBar\\ 的 aboutToShow() 信号。我想不出一个原因(无论如何对我来说)你不能在这些信号中做你需要的事情,因为你在插槽中通过了 QAction*。但是,如果您必须以其他方式进行操作,您可以按照我上面显示的方式进行操作,您可能不想使用 QActionGroup,因为无线电分组是它的设计目的。 (您可以通过不将"可检查"的项目添加到组中来解决此问题。
这把它们放在一起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | template <class Function> class QMenuBarIterator { QMenuBar *i_barP; void iterate_sub(Function f, size_t tabsL, QMenu* m) { foreach (QAction *action, m->actions()) { f(tabsL, action); if (action->menu()) { iterate_sub(f, tabsL + 1, action->menu()); } } } public: QMenuBarIterator(QMenuBar *barP) : i_barP(barP) {} virtual void operator()(size_t levelL, QAction *actionP) { } void iterate(Function f) { QList<QMenu *> menuBar = i_barP->findChildren<QMenu *>(); foreach (QMenu* m, menuBar) { f(0, m->menuAction()); iterate_sub(f, 1, m); } } }; /***************************************************************************/ class CMenuLogger { public: void operator()(size_t tabsL, QAction *action) { SuperString tabStr(GetIndentString(tabsL)); // returns a string with tabsL tab characters in it if (action->isSeparator()) { qDebug("%s-------------------", tabStr.utf8Z()); } else { qDebug("%s%s (%s)", tabStr.utf8Z(), qUtf8Printable(action->text()), qUtf8Printable(action->objectName())); } } }; |
然后在你的主目录中:
1 2 3 4 5 | { QMenuBarIterator<CMenuLogger> bar(ui->menuBar); bar.iterate(CMenuLogger()); } |
qobject_cast 失败的原因是只有三个以 QMenuBar 作为父级的 QAction。其他三个是不同的 QObjects(我猜是三个 QMenus),所以转换失败。与这些菜单关联的 QActions 位于这些菜单下,而不是根 QMenuBar。我不明白为什么您不能通过递归地遍历 QMenus 来构建 QActions 的主列表。
如果您在使用已知菜单,则可能只使用 UI 定义中的 QAction 指针,这可能不会触发父菜单。如果您尝试自动化测试,您所需的 QAction 上的 trigger() 可能与您需要的一样详细。如果您尝试对用户操作做出响应,那么修改工具栏可能是一种更好的上下文反馈方式,因为它不会破坏焦点。有关您实际尝试完成的任务的更多详细信息会有所帮助。