Tomáš Zato Tomáš Zato - 11 months ago 61
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 Source

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