QT 上下文菜单内存泄露之QMainWindow
创始人
2024-08-01 21:51:13
0

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

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

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

问题重现

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

  1. #include  
  2. class MainWindow : public QMainWindow  
  3. {  
  4.     Q_OBJECT  
  5. public:  
  6.     explicit MainWindow(QWidget *parent = 0);  
  7. private slots:  
  8.     void onButtonClicked();  
  9. };  
  10. MainWindow::MainWindow(QWidget *parent)  
  11. {  
  12.     addToolBar("ToolBar");  
  13.     addDockWidget(Qt::LeftDockWidgetArea, new QDockWidget("DockWidget"));  
  14.     QPushButton * btn = new QPushButton("dump object tree");  
  15.     setCentralWidget(btn);  
  16.     connect(btn, SIGNAL(clicked()), SLOT(onButtonClicked()));  
  17. }  
  18. void MainWindow::onButtonClicked()  
  19. {  
  20.     dumpObjectTree();  
  21. }  
  22. #include "main.moc"  
  23. int main(int argc, char *argv[])  
  24. {  
  25.     QApplication a(argc, argv);  
  26.     MainWindow w;  
  27.     w.show();  
  28.  
  29.     return a.exec();  

原因

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

  1. void QMainWindow::contextMenuEvent(QContextMenuEvent *event)  
  2. {  
  3.     event->ignore();  
  4. ...  
  5.     QMenu *popup = createPopupMenu();  
  6.     if (popup) {  
  7.         if (!popup->isEmpty()) {  
  8.             popup->setAttribute(Qt::WA_DeleteOnClose);  
  9.             popup->popup(event->globalPos());  
  10.             event->accept();  
  11.         } else {  
  12.             delete popup;  
  13.         }  
  14.     }  

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

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

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

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

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

  1. void QMainWindow::contextMenuEvent(QContextMenuEvent *event)  
  2. {  
  3.     event->ignore();  
  4. ...  
  5.     QMenu *popup = createPopupMenu();  
  6.     if (popup && !popup->isEmpty()) {  
  7.         popup->exec(event->globalPos());  
  8.         event->accept();  
  9.     }  
  10.     delete popup;  

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

进一步学习

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

想想?如何让菜单隐藏

鼠标:点击菜单外区域

键盘:按下Esc键等

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

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

  1. void QMenu::keyPressEvent(QKeyEvent *e)  
  2. {  
  3.     Q_D(QMenu);  
  4.     d->updateActionRects();  
  5.     int key = e->key();  
  6. ...  
  7.     bool key_consumed = false;  
  8.     switch(key) {  
  9.     case Qt::Key_Escape:  
  10.         key_consumed = true;  
  11.         {  
  12.             QPointer caused = d->causedPopup.widget;  
  13.             d->hideMenu(this); // hide after getting causedPopup  
  14.             if (QMenuBar *mb = qobject_cast(caused)) {  
  15.                 mb->d_func()->setCurrentAction(d->menuAction);  
  16.                 mb->d_func()->setKeyboardMode(true);  
  17.             }  
  18.         }  
  19.         break;鼠标在菜单区域外按键,调用了hideUpToMenuBar(进而调用hideMenu)   
  20. void QMenu::mousePressEvent(QMouseEvent *e)  
  21. {  
  22.     Q_D(QMenu);  
  23. ...  
  24.     if (!rect().contains(e->pos())) {  
  25.          if (d->noReplayFor  
  26.              && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))  
  27.              setAttribute(Qt::WA_NoMouseReplay);  
  28.          if (d->eventLoop) // synchronous operation  
  29.              d->syncAction = 0;  
  30.         d->hideUpToMenuBar();  
  31.         return;  
  32.     }  

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

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

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

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...