20

我正在一个项目中工作,我需要自动打开(显示或弹出)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()获得菜单栏中的菜单列表时,我的意思是,我"File, Edit, Help"从我的 QMenuBar 获得,在这种情况下 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()时,它总是会导致我出现分段错误。

我一直在搜索,我在这里发现,在获取菜单栏操作列表后,可以询问QAction::menu()(如果项目是菜单,则返回有效的 QMenu 指针)知道项目是否是 QMenu,如果是,可以重复获取该菜单的操作列表,并继续迭代。但这对我不起作用,我希望

QList<Object*> lst2 = QMenuBar::findChildren<QAction*>();

每个元素“文件,编辑帮助”QAction::menu()返回一个有效的菜单指针,所以我可以获得每个菜单的操作列表,但这对我来说根本不起作用。

4

4 回答 4

25

枚举a的正确方法QMenu是使用actions()函数,但是有一个catch——有些动作是子菜单,需要递归迭代。事实上,每个QMenu都与 a 相关联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()));
        }
    }
}
于 2016-07-18T06:34:50.013 回答
8

下面是如何遍历菜单栏中的每个菜单项,它还会查找下面的任何菜单,因此这里不需要递归调用。

// 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,因为无线电分组是它的设计目的。(您可以通过不添加“可检查”的项目来解决这个问题

于 2013-12-25T12:22:47.673 回答
0

qobject_cast 失败的原因是只有三个以 QMenuBar 作为父级的 QAction。其他三个是不同的 QObjects(我猜是三个 QMenus),所以转换失败。与这些菜单关联的 QActions 位于这些菜单下,而不是根 QMenuBar。我不明白为什么你不能通过递归遍历 QMenus 来构建 QActions 的主列表。

如果您在使用可能不会触发父菜单的已知菜单,则可以只使用 UI 定义中的 QAction 指针。如果您尝试自动化测试,您所需的 QAction 上的 trigger() 可能与您需要的一样详细。如果您尝试对用户操作做出响应,那么修改工具栏可能是一种更好的上下文反馈方式,因为它不会破坏焦点。有关您实际要完成的工作的更多详细信息会有所帮助。

于 2012-02-22T21:52:39.823 回答
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());
}
于 2017-07-16T21:51:45.677 回答