我在一个项目中工作,我需要自动打开(显示或弹出)QMenuBar中的项目。
假设我有下一个菜单栏:
File Edit Help
-op1 -op1 -op1
-op2 -op2 -op2
要设置操作(显示与该操作相关的菜单),我使用:
menuBar->setActiveAction(mymenuactionpointer);
据我所知,我可以使用以下其中一项来获取指向QMenuBar元素的指针列表:
QMenuBar::actions();
或
QList<Object*> lst1 = QMenuBar::findChildren<QObject*>();
QList<Object*> lst2 = QMenuBar::findChildren<QAction*>();
当我使用QMenuBar::findChildren<QAction*>()
或MenuBar::actions()
时,我得到菜单栏中的菜单列表,我的意思是,我从QMenuBar获得"File, Edit, Help"
,在这种情况下,QList的大小是3。
当我使用QMenuBar::findChildren<QObject*>()
时,我得到了一个大小为6的QObject列表,它是菜单栏中正确的项目数。但是,我已经尝试过演员QAction *
QAction *a = (QAction *)lst1.at(0);
QAction *a = qobject_cast<QAction*>(lst1.at(0));
QAction *a = dynamic_cast<QAction*>(lst1.at(0));
在所有这些情况下,a
不是NULL,但是当我尝试获取操作名称QAction::title()
时,它总是导致我出现分段错误。
我一直在搜索,我发现here在获取菜单栏操作列表后,可以询问QAction::menu()
(如果项目是菜单则返回有效的QMenu指针)以了解是否item是QMenu,如果是,可以重复获取该菜单的动作列表,并继续迭代。但这对我不起作用,我期待
QList<Object*> lst2 = QMenuBar::findChildren<QAction*>();
每个元素“文件,编辑帮助”QAction::menu()
都会返回一个有效的菜单指针,因此我可以获取每个菜单的操作列表,但这对我来说根本不起作用。
我非常感谢您的时间和帮助,我希望这个问题可以帮助更多人。我真的很难过。
提前致谢。
答案 0 :(得分:19)
枚举QMenu
的正确方法是使用actions()
函数,但有一个问题 - 一些操作是子菜单,它们需要递归迭代。事实上,每个QMenu
都与QAction
相关联,并且它们都互为指针 - 请参阅QMenu::menuAction()和QAction::menu()。
了解每个QMenu还与QAction相关至关重要。所以知道,正确的实现如下:
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 :(得分:7)
下面是如何遍历菜单栏中的每个菜单项,它还会搜索下面的任何菜单,因此这里不需要递归调用。
// 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,因为无线电分组是它的设计目的。 (你可以通过不添加“可检查”的项目来解决这个问题。
答案 2 :(得分:0)
qobject_cast失败的原因是QMenuBar只有三个QAction作为父级。其他三个是不同的QObjects(我的猜测是三个QMenus),所以演员表失败了。然后,与那些菜单相关联的QActions不在根QMenuBar之下。我不明白为什么你不能通过递归迭代QMenus来建立QActions的主列表。
如果您在可能不会触发父菜单的已知菜单之后,可能能够仅使用UI定义中的QAction指针。如果您正在尝试自动化测试,那么您所需的QAction上的trigger()可能就像您需要的那样详细。如果您正在尝试响应用户操作而执行操作,则修改工具栏可能是更好的上下文反馈方式,因为它不会破坏焦点。关于你实际想要完成什么的更多细节会有所帮助。
答案 3 :(得分:0)
将这一切放在一起:
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()));
}
}
};
然后在你的主要:
{
QMenuBarIterator<CMenuLogger> bar(ui->menuBar);
bar.iterate(CMenuLogger());
}