Prevent a QMenu from closing when one of its QAction is triggered

Viewed 13326

I'm using a QMenu as context menu. This menu is filled with QActions. One of these QActions is checkable, and I'd like to be able to check/uncheck it without closing the context menu (and having to re-open it again to choose the option that I want).

I've tried disconnecting the signals emitted by the checkable QAction with no luck.

Any ideas? Thanks.

7 Answers

Use a QWidgetAction and QCheckBox for a "checkable action" which doesn't cause the menu to close.

QCheckBox *checkBox = new QCheckBox(menu);
QWidgetAction *checkableAction = new QWidgetAction(menu);
checkableAction->setDefaultWidget(checkBox);
menu->addAction(checkableAction);

In some styles, this won't appear exactly the same as a checkable action. For example, for the Plastique style, the check box needs to be indented a bit.

Here are couple ideas I've had... Not sure at all they will work tho ;)

1) Try to catch the Event by using the QMenu's method aboutToHide(); Maybe you can "Cancel" the hide process ?

2) Maybe you could consider using an EventFilter ?

Try to have a look at : http://qt.nokia.com/doc/4.6/qobject.html#installEventFilter

3) Otherwise you could reimplement QMenu to add your own behavior, but it seems a lot of work to me...

Hope this helps a bit !

Starting from baysmith solution, the checkbox didn´t work as I was expecting, because I was connecting to the action triggered(), rather than connecting to the checkbox toggled(bool). I´m using the code to open a menu with several checkboxes when I press a button :

           QMenu menu;

            QCheckBox *checkBox = new QCheckBox("Show Grass", &menu);
            checkBox->setChecked(m_showGrass);
            QWidgetAction *action = new QWidgetAction(&menu);
            action->setDefaultWidget(checkBox);
            menu.addAction(action);
            //connect(action, SIGNAL(triggered()), this, SLOT(ToggleShowHardscape_Grass()));
            connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(ToggleShowHardscape_Grass()));

            menu.exec(QCursor::pos() + QPoint(-300, 20));

For my use case, this works like a charm

Subclass QMenu and override setVisible. You can utilize activeAction() to know if an action was selected and the visible arg to see if the QMenu is trying to close, then you can override and call QMenu::setVisible(...) with the value you want.

class ComponentMenu : public QMenu
{
public:
    using QMenu::QMenu;

    void setVisible(bool visible) override
    {
        // Don't hide the menu when holding Shift down
        if (!visible && activeAction())
            if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
                return;

        QMenu::setVisible(visible);
    }
};
Related