Как перебрать действия меню в Qt? - PullRequest
17 голосов
/ 22 февраля 2012

Я работаю в проекте, где мне нужно автоматически открывать (показывать или всплывать) элементы в 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(), я получил список меню в menubar, я имею в виду, я получил "File, Edit, Help" из моего QMenuBar, размер QList в этом случае равен 3.

Когда я использую QMenuBar::findChildren<QObject*>(), я получаю список QObject размера 6, который является правильным количеством пунктов в строке меню. Тем не менее, я попытался привести к 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, если элемент является меню), чтобы узнать, если item является QMenu, если да, можно повторить получение списка действий этого меню и продолжить итерацию. Но это не работает для меня, я ожидал, что для

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

каждый элемент «Файл, Редактировать справку» QAction::menu() возвращает действительный указатель меню, чтобы я мог получить список действий каждого меню, но у меня это совсем не работает.

Я очень ценю ваше время и вашу помощь, я надеюсь, что этот вопрос поможет большему количеству людей. Мне действительно тяжело с этим.

Заранее спасибо.

Ответы [ 4 ]

20 голосов
/ 18 июля 2016

Правильный способ перечисления 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()));
        }
    }
}
7 голосов
/ 25 декабря 2013

Ниже описывается, как выполнять итерацию по каждому пункту меню в строке меню, он также будет искать любые меню внизу, поэтому здесь нет необходимости в рекурсивном вызове.

// 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 или если вам нужно знать, когда появится меню, вам понадобитсяСигнал aboutToShow () в QMenuBar.Я не могу придумать причину (для меня, во всяком случае), что вы не можете делать то, что вам нужно в этих сигналах, так как вы прошли QAction * в слоте.Но если вы ДОЛЖНЫ сделать это иначе, вы можете сделать это так, как я показал выше, вы просто можете не захотеть использовать QActionGroup, потому что радиогруппировка - это то, для чего она предназначена.(Вы можете обойти это, не добавляя в группу элементы, которые можно проверить).

0 голосов
/ 17 июля 2017

это складывает все вместе:

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());
}
0 голосов
/ 23 февраля 2012

Причина сбоя qobject_cast заключается в том, что существует только три QActions с QMenuBar в качестве родителя. Остальные три - это разные объекты QObject (я думаю, это три QMenus), поэтому приведение не выполняется. QActions, связанные с этими меню, находятся под ними, а не с корневым QMenuBar. Я не понимаю, почему вы не можете создать основной список QActions путем рекурсивной итерации QMenus.

Вы можете иметь возможность просто использовать указатель QAction из вашего определения пользовательского интерфейса, если вы находитесь после известного меню, которое может не вызывать родительские меню. Если вы пытаетесь автоматизировать тестирование, триггер () для желаемого QAction, вероятно, будет настолько подробным, насколько вам нужно. Если вы пытаетесь что-то сделать в ответ на действия пользователя, изменение панелей инструментов, вероятно, является лучшим средством контекстной обратной связи, так как оно не нарушает фокус. Некоторые подробности о том, чего вы на самом деле пытаетесь достичь, помогут.

...