QT 上下文菜单内存泄露之QMainWindow

移动开发
本文介绍的是QT 上下文菜单内存泄露之QMainWindow,根据不同版本进行测试,并解决问题。先来看内容。

QT 上下文菜单内存泄露之QMainWindow 是本人要介绍的内容,先来看内容。创建Qt工程,基于QMainwindow,什么也不做,程序会自带一个上下文菜单。

不断点击鼠标右键,菜单将反复出现,此时我用任务管理器查看其内存变化,发现每次不断增加,请问大家这是Qt内存泄漏吗???我用MFC,CB均没有发现类此错误。

Qt 4.7.0 和 4.7.3下可以重现该问题,在Qt 4.6.3下不存在该问题。可以确定是Qt的一个bug。

问题重现

在工具栏或停靠窗口中点击右键(弹出上下文菜单),多点击几次,然后点击按钮。观察控制台输出,可以看到很多个 QMenu 对象。

#include <QtGui> 
class MainWindow : public QMainWindow  
{  
    Q_OBJECT  
public:  
    explicit MainWindow(QWidget *parent = 0);  
private slots:  
    void onButtonClicked();  
};  
MainWindow::MainWindow(QWidget *parent)  
{  
    addToolBar("ToolBar");  
    addDockWidget(Qt::LeftDockWidgetArea, new QDockWidget("DockWidget"));  
    QPushButton * btn = new QPushButton("dump object tree");  
    setCentralWidget(btn);  
    connect(btn, SIGNAL(clicked()), SLOT(onButtonClicked()));  
}  
void MainWindow::onButtonClicked()  
{  
    dumpObjectTree();  
}  
#include "main.moc"  
int main(int argc, char *argv[])  
{  
    QApplication a(argc, argv);  
    MainWindow w;  
    w.show();  
 
    return a.exec();  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

原因

既然是QMainWindow的上下文菜单问题,直接看 contextMenuEvent 事件处理函数吧。

void QMainWindow::contextMenuEvent(QContextMenuEvent *event)  
{  
    event->ignore();  
...  
    QMenu *popup = createPopupMenu();  
    if (popup) {  
        if (!popup->isEmpty()) {  
            popup->setAttribute(Qt::WA_DeleteOnClose);  
            popup->popup(event->globalPos());  
            event->accept();  
        } else {  
            delete popup;  
        }  
    }  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

看仔细喽,这儿设置了 Qt::WA_DeleteOnClose 属性。

有什么用?设置该属性后,当我们调用该对象的 close() 成员时,隐藏(hide)窗口同时会删除(delete)该对象

有什么问题?问题出在,实际上隐藏菜单时没有 调用菜单的close(),而是 调用的hide()的成员。

调用hide()而不是close(),是的该属性不能发挥任何作用,进而导致内存泄露(Qt 之 show,hide,setVisible,setHidden,close 等小结 )。

为了对比,我们看看Qt4.6.3的源码部分:

void QMainWindow::contextMenuEvent(QContextMenuEvent *event)  
{  
    event->ignore();  
...  
    QMenu *popup = createPopupMenu();  
    if (popup && !popup->isEmpty()) {  
        popup->exec(event->globalPos());  
        event->accept();  
    }  
    delete popup;  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

而这个,也就是我们的比较理想的答案了。

进一步学习

前面说了,菜单隐藏时调用的是hide() 成员,而不是close() 成员。有神马依据??

想想?如何让菜单隐藏

鼠标:点击菜单外区域

键盘:按下Esc键等

这样就比较明朗了,对吧,直接看这两个事件处理函数

键盘的按键事件(调用了hideMenu)

void QMenu::keyPressEvent(QKeyEvent *e)  
{  
    Q_D(QMenu);  
    d->updateActionRects();  
    int key = e->key();  
...  
    bool key_consumed = false;  
    switch(key) {  
    case Qt::Key_Escape:  
        key_consumed = true;  
        {  
            QPointer<QWidget> caused = d->causedPopup.widget;  
            d->hideMenu(this); // hide after getting causedPopup  
            if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {  
                mb->d_func()->setCurrentAction(d->menuAction);  
                mb->d_func()->setKeyboardMode(true);  
            }  
        }  
        break;鼠标在菜单区域外按键,调用了hideUpToMenuBar(进而调用hideMenu)   
void QMenu::mousePressEvent(QMouseEvent *e)  
{  
    Q_D(QMenu);  
...  
    if (!rect().contains(e->pos())) {  
         if (d->noReplayFor  
             && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))  
             setAttribute(Qt::WA_NoMouseReplay);  
         if (d->eventLoop) // synchronous operation  
             d->syncAction = 0;  
        d->hideUpToMenuBar();  
        return;  
    }  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

前面都调用了hideMenu,从名字也能猜猜它想干什么:

void QMenuPrivate::hideMenu(QMenu *menu, bool justRegister)  
{  
...  
        menu->hide();  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

小结:QT 上下文菜单内存泄露QMainWindow 的内容介绍完了,希望本文对你有所帮助!

责任编辑:zhaolei 来源: 互联网
相关推荐

2012-08-01 09:58:12

Mountain Li操作系统

2017-05-11 14:00:02

Flask请求上下文应用上下文

2012-12-31 10:01:34

SELinuxSELinux安全

2022-09-14 13:13:51

JavaScript上下文

2018-06-17 08:38:17

微软Windows应用程序

2022-09-15 08:01:14

继承基础设施基础服务

2023-07-11 10:02:23

2024-09-30 14:10:00

2022-10-28 16:24:33

Context上下文鸿蒙

2017-12-17 17:01:23

限界上下文系统模型

2020-07-24 10:00:00

JavaScript执行上下文前端

2021-07-26 07:47:36

Cpu上下文进程

2021-05-09 21:50:48

项目实践上下文

2022-10-31 15:34:30

python装饰器内存泄漏

2021-07-20 19:30:05

微软Windows 11Windows

2012-07-30 16:29:40

架构架构模式.NET

2019-05-06 14:36:48

CPULinux寄存器

2010-02-25 17:04:54

WCF实例上下文

2022-04-24 15:37:26

LinuxCPU

2023-12-10 13:37:23

Python编程上下文管理
点赞
收藏

51CTO技术栈公众号