何时以及如何正确销毁 QMenu 上下文菜单?
When and how to properly destroy QMenu context menu?
我允许自定义上下文菜单出现在 table 上。这是生成菜单的方式,使用接受目标小部件和坐标的通用函数:
#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);
}
问题是 QMenu* menu
永远不会被销毁并从内存中删除。即使在隐藏之后,它仍然作为 MainWindow
的子项存在。
我该怎么办?我可以将菜单设置为自行删除吗?或者我应该重复使用相同的菜单实例还是将其保存到相同的指针中?
从你的代码来看,似乎 menu
应该在这个事件发生后删除?
// Notify window about clicking
QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);
Can I set the menu to delete itself?
是的,您可以这样 object delete itself:
// Notify window about clicking
QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);
QObject::connect(menu, &QMenu::triggered, menu, &QMenu::deleteLater);
如果您担心这些插槽被调用的顺序,请参阅 this
Or should I reuse the same instance of menu or maybe save it into same
pointer?
好吧,你可以这样做
//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);
....
}
至于 MainWindow::~MainWindow()
析构函数,它将负责 menu
的清理工作。由于 MainWindow
(这是 QObject
派生的 class) automatically deletes 所有 children
最后,您可以简单地将 menu
作为 MainWindow
的成员,并且每当您需要为 menu
执行新操作时,您可以使用 QMenu::clear
删除所有现有操作。
//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);
}
隐藏时可以删除QMenu
。为此,我设计了事件过滤器 class:
#ifndef DELETEONHIDEFILTER_H
#define DELETEONHIDEFILTER_H
#include <QObject>
#include <QEvent>
class DeleteOnHideFilter : public QObject
{
Q_OBJECT
public:
explicit DeleteOnHideFilter(QObject *parent = 0) : QObject(parent) {}
protected slots:
bool eventFilter(QObject *obj, QEvent *event) override {
if(event->type() == QEvent::Hide) {
obj->deleteLater();
}
return false;
}
};
#endif // DELETEONHIDEFILTER_H
它也可以用于其他对象。
没必要这么复杂。已经这样了:
menu->setAttribute(Qt::WA_DeleteOnClose);
这样,当 QMenu 关闭时,一旦再次进入事件循环,class 就会被删除。触发动作或刚刚关闭弹出窗口都没有关系。
为了证明我的答案,您可以通过检查菜单创建时间以及是否使用相同地址触发 'deleted' 消息来自行测试:
qDebug() << "created" << (qintptr)menu;
connect(menu, &QMenu::destroyed,
this, [menu]() { qDebug() << "deleted" << (qintptr)menu; });
我允许自定义上下文菜单出现在 table 上。这是生成菜单的方式,使用接受目标小部件和坐标的通用函数:
#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);
}
问题是 QMenu* menu
永远不会被销毁并从内存中删除。即使在隐藏之后,它仍然作为 MainWindow
的子项存在。
我该怎么办?我可以将菜单设置为自行删除吗?或者我应该重复使用相同的菜单实例还是将其保存到相同的指针中?
从你的代码来看,似乎 menu
应该在这个事件发生后删除?
// Notify window about clicking
QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);
Can I set the menu to delete itself?
是的,您可以这样 object delete itself:
// Notify window about clicking
QObject::connect(menu, &QMenu::triggered, this, &MainWindow::menuClicked);
QObject::connect(menu, &QMenu::triggered, menu, &QMenu::deleteLater);
如果您担心这些插槽被调用的顺序,请参阅 this
Or should I reuse the same instance of menu or maybe save it into same pointer?
好吧,你可以这样做
//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);
....
}
至于 MainWindow::~MainWindow()
析构函数,它将负责 menu
的清理工作。由于 MainWindow
(这是 QObject
派生的 class) automatically deletes 所有 children
最后,您可以简单地将 menu
作为 MainWindow
的成员,并且每当您需要为 menu
执行新操作时,您可以使用 QMenu::clear
删除所有现有操作。
//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);
}
隐藏时可以删除QMenu
。为此,我设计了事件过滤器 class:
#ifndef DELETEONHIDEFILTER_H
#define DELETEONHIDEFILTER_H
#include <QObject>
#include <QEvent>
class DeleteOnHideFilter : public QObject
{
Q_OBJECT
public:
explicit DeleteOnHideFilter(QObject *parent = 0) : QObject(parent) {}
protected slots:
bool eventFilter(QObject *obj, QEvent *event) override {
if(event->type() == QEvent::Hide) {
obj->deleteLater();
}
return false;
}
};
#endif // DELETEONHIDEFILTER_H
它也可以用于其他对象。
没必要这么复杂。已经这样了:
menu->setAttribute(Qt::WA_DeleteOnClose);
这样,当 QMenu 关闭时,一旦再次进入事件循环,class 就会被删除。触发动作或刚刚关闭弹出窗口都没有关系。
为了证明我的答案,您可以通过检查菜单创建时间以及是否使用相同地址触发 'deleted' 消息来自行测试:
qDebug() << "created" << (qintptr)menu;
connect(menu, &QMenu::destroyed,
this, [menu]() { qDebug() << "deleted" << (qintptr)menu; });