Nicolas Holthaus
Nicolas Holthaus

Reputation: 8293

How do you close a QMenu when its QWidgetAction is triggered?

I have a QMenu for which I've created a QColorModel action widget (It's effectively just a QStandardItemModel). My desired behavior is that when a user clicks one of the colors in the model, that the action should trigger, and the menu close. However, it doesn't seem to do that, even when I trigger the action manually.

I've tried manually hiding the menu, but it's a kludge because it won't hide parent menus which th menu may be attached to.

Here's the relevant section of code:

// color menu
m_colorMenu = new QMenu("color", this);
m_colorView = new QColorView(m_colorMenu);

m_colorViewAction = new QWidgetAction(m_colorMenu);
m_colorViewAction->setDefaultWidget(m_colorView);

m_colorView->setModel(new QStandardColorModel);
connect(m_colorView, &QColorView::clicked, [&](QModelIndex index)
{
    QColor color = qvariant_cast<QColor>(index.data(Qt::DecorationRole));
    if (m_pen.color() != color)
    {
        m_pen.setColor(color);
        drawIcon();
        drawColorIcon();
        update();
    }
    //this->hide();                // kludge, didn't close all parent menus
    m_colorViewAction->trigger();  // doesn't seem to cause menu closure

});

m_colorMenu->addAction(m_colorViewAction);

EDIT

I've also tried adding something to the effect of:

QMenu* menu = m_colorMenu;
do
{
    menu->close();
    menu = dynamic_cast<QMenu*>(menu->parent());
} while (menu);

but it also is fragile/kludgey because it assumes a) all widgets are properly parented, and b) that all the parents are actually supposed to be menus. In my case, they aren't.

Upvotes: 1

Views: 1634

Answers (1)

Nicolas Holthaus
Nicolas Holthaus

Reputation: 8293

If the containing menus aren't in the parentage tree, and the menu you want to close isn't the top level menu, there is no easy way to do this. That said, there is:

THE NUCLEAR OPTION

Adding this to the end of the lambda function

auto topLevelWidgets = qApp->topLevelWidgets();
for (auto widget : topLevelWidgets)
{
    QMenu* menu = dynamic_cast<QMenu*>(widget);
    if (menu)
    {
        menu->close();
    }
}

will cause ALL top level menus to close once the action is triggered. This is a relatively OK way to accomplish what you want because:

  1. one of the top level menus will contain the menu in question, and
  2. never say never, but I can't think of a single case where you would have (or want) more than one menu open at a time, so most likely the only open menu tree you would close is the intended one.

Upvotes: 2

Related Questions