Tomáš Zato Tomáš Zato - 3 months ago 14
C++ Question

When and how to properly destroy QMenu context menu?

I allow custom context menu to appear over a table. This is how the menu is generated, using a generic function that accepts target widget and coordinates:

#include <QMenu>
void MainWindow::makeContextMenu(const QPoint& pos, QWidget* target)
{
QMenu *menu = new QMenu(this);
menu->addAction(new QAction("Action 1", menu));
menu->addAction(new QAction("Action 2", menu));
menu->addAction(new QAction("Action 3", menu));
// Notify window about clicking
QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);
// If this is a scroll area, map coordinates to real app coordinates
if(QAbstractScrollArea* area = dynamic_cast<QAbstractScrollArea*>(target))
menu->popup(area->viewport()->mapToGlobal(pos));
else
menu->popup(pos);
}


The problem is that the
QMenu* menu
never gets destroyed and removed from memory. It persists as
MainWindow
's child even after it's hidden.

What should I do? Can I set the menu to delete itself? Or should I reuse the same instance of menu or maybe save it into same pointer?

Answer

From your code, it seems like menu should be deleted after this event has taken place?

// Notify window about clicking
QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);

Can I set the menu to delete itself?

Yes, you can have the object delete itself like this:

// Notify window about clicking
QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);
QObject::connect(menu, &QMenu::triggered, menu, &QMenu::deleteLater);

If you are worried about the order of those slots being called, see this


Or should I reuse the same instance of menu or maybe save it into same pointer?

Well, you can do something like

//Your constructor
MainWindow::MainWindow(....)
{
    menu = nullptr;
    ....
}

//Make context Menu
void MainWindow::makeContextMenu(const QPoint& pos, QWidget* target)
{
    if(menu)
        delete menu; 
    menu = new QMenu(this);
    ....
}

As for the MainWindow::~MainWindow() destructor, it will take care of the menu's clean up. Since MainWindow (which is a QObject derived class) automatically deletes all children


Lastly, you can simply have the menu as a member of MainWindow, and whenever you need to have fresh actions for menu, you can use QMenu::clear to delete all existing actions.

//Your constructor
MainWindow::MainWindow(....)
{
    menu = new QMenu(this);
    ....
}

void MainWindow::makeContextMenu(const QPoint& pos, QWidget* target)
{
    menu->clear();
    //QMenu *menu = new QMenu(this);
    menu->addAction(new QAction("Action 1", menu));
    menu->addAction(new QAction("Action 2", menu));
    menu->addAction(new QAction("Action 3", menu));
    // Notify window about clicking
    QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);
    // If this is a scroll area, map coordinates to real app coordinates
    if(QAbstractScrollArea* area = dynamic_cast<QAbstractScrollArea*>(target))
        menu->popup(area->viewport()->mapToGlobal(pos));
    else
        menu->popup(pos);
}
Comments