Подтвердить что ты не робот

Как выполнить итерацию в действиях меню в Qt?

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

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

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

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

Спасибо заранее.

4b9b3361

Ответ 1

Правильный способ перечисления QMenu заключается в использовании функций actions(), но есть catch - некоторые из этих действий являются подменю, и их нужно рекурсивно повторять. Фактически, каждый 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()));
        }
    }
}

Ответ 2

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

// 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 из-за группировки радио, для чего он предназначен. (вы можете обойти это, не добавляя элементы, которые "проверяются" в группу.

Ответ 3

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

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

Ответ 4

это ставит все вместе:

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());
}