Qt下hide()与close()相关的bug一例

本文深入探讨了Qt4.6中使用QMenu::exec()引发内存泄露的问题,并提供了修复方法。通过将QMenu::exec()替换为QMenu::popup(),解决了内存泄露问题,同时适用于QTextEdit/QPlainTextEdit/QLineEdit/QMessageBox等多种组件。

本文是QMainWindow上下文菜单内存泄露(QTBUG)一文 的续篇,所以你很可能需要先看上文。

前传

问题起源于:QTBUG-7902

Qt 4.6下,下面的小程序在显示上下文菜单的情况下会导致程序崩溃。

#include <QApplication>
#include <QTextEdit>
#include <QTimer>

int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  QTextEdit *te = new QTextEdit;
  te->show();
  te->setText("right click here now");

  QTimer ti;
  te->connect(&ti, SIGNAL(timeout()), te, SLOT(deleteLater()));
  ti.start(3000);

  return qApp->exec();
}

这个问题是由于QMenu::exec()开启的局部事件循环造成的副作用。【至于局部事件循环可能造成的各种潜在问题,可访问乱谈Qt事件循环嵌套

2010.2.9日的 b7af368e86874d71ffc9071c9ef009814d6a3467 修复这个问题

修复

采用QMenu::popup() 取代 QMenu::exec()

这也是我们在QMainWindow上下文菜单内存泄露(QTBUG)一文中看到的 QMainWindow的上下文菜单之所以那么改动的原因。

内存泄漏

当看了 b7af368e86874d71ffc9071c9ef009814d6a3467 以后,我发现这个问题远比我想想的严重,除了QMainWindow之外,QTextEdit/QPlainTextEdit/QLineEdit/QMessageBox 等都会存在这个问题。

于是提交了 QTBUG-22817

如何修复?

也就是我们在QMainWindow上下文菜单内存泄露(QTBUG)一文提到的,只需要改动一行代码即可:

--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -500,7 +500,7 @@ void QMenuPrivate::hideMenu(QMenu *menu, bool justRegi
     menu->blockSignals(false);
 #endif // QT_NO_EFFECTS
     if (!justRegister)
-        menu->hide();
+        menu->close();
 }

参考

我在实行PyQt5时,出现主界面,但是点击按钮没有显示图片,下面是我的代码 import sys from PyQt5.QtWidgets import QApplication,QMainWindow,QDialog,QMessageBox,QGridLayout from PyQt5.QtCore import Qt from Database import * from PyQt5 import QtCore import prc7 #主界面 import Main_wiond #登陆注册界面 from PyQt5.QtCore import QSettings from matplotlib import pyplot as plt import matplotlib #避免中文乱码 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['font.size'] = 20 #设置整体字体大小 plt.rcParams['axes.unicode_minus'] = False #从matplotlib 导入Qt集成相关的类 matplotlib.use("Qt5Agg") #强制使用Qt5后端进行渲染 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure class MyFigure(FigureCanvas): def __init__(self,width=5,height=4,dpi=100): #第一步,创建一个Figure facecolor背景色 self.fig=Figure(figsize=(width,height),dpi=dpi,facecolor="#458cff") #第二部,激活Figure窗口,这一步不能少,否则不显示图形 super(MyFigure,self).__init__(self.fig) class Login_Register(QMainWindow): def __init__(self): super().__init__() #初始化父类的属性 self.ui=Main_wiond.Ui_MainWindow() #实例化登陆界面窗口类对象 self.win = QMainWindow() #窗口对象 self.win.setWindowFlags(Qt.FramelessWindowHint)#去除边框 self.win.setAttribute(Qt.WA_TranslucentBackground) self.ui.setupUi(self.win) #这个语法取决于你的窗口里面函数定义格式 self.ui.zhuce.hide()#初始化隐藏你的注册界面 self.loadsetings()# self.win.show() def D_change_z(self): #登录切换注册 self.ui.denglu.hide() self.ui.zhuce.show() def Z_change_D(self): #注册切换登录 self.ui.denglu.show() self.ui.zhuce.hide() def close_win(self):#关闭窗口 self.ui.pushButton_3.clicked.connect(self.win.close)#关闭符合按钮的对象名称 def loadsetings(self):#加载保存账号密码 setting = QSettings("admin", "lyjd") #读取记住密码状态,默认为False,setting.value(key,defaultvalue默认值,type) remember = setting.value("remember",False,type=bool) self.ui.checkBox.setChecked(remember)#选中状态 if remember: username = setting.value("username","",type=str) self.ui.lineEdit.setText(username) #同样也可以加载密码 password = setting.value("password","",type=str) self.ui.lineEdit_2.setText(password) def savesetings(self):#保存用户名和密码 setting = QSettings("admin","lyjd") #保存记住密码的状态 remember = self.ui.checkBox.isChecked() setting.setValue("remember",remember) #保存用户名密码 if remember: user = self.ui.lineEdit.text() #用户名 setting.setValue("username",user) #如果不需要记住密码,可以注释下方代码 password = self.ui.lineEdit_2.text()#密码 setting.setValue("password", password) else: #如果不记住,可以删除保存的账户密码 setting.remove("username") setting.remove("password") def login_user(self):#登录验证 user = self.ui.lineEdit.text() password = self.ui.lineEdit_2.text() self.savesetings()#保存设置(无论登录成功否都会保存记住密码选择) if password_check_sql(user):#验证账号是否存在 data=password_check_sql(user) if password==data[0].get("password"):#获取账号对应的密码 QMessageBox.information(self.win,"提示","登录成功") #切换主界面 self.home=Home() self.win.hide() else: QMessageBox.critical(self.win,"错误","登录失败") else: QMessageBox.critical(self.win,"错误","账号不存在") def res_user(self):#注册验证 user = self.ui.lineEdit_3.text() password = self.ui.lineEdit_4.text() check_password = self.ui.lineEdit_5.text() if not user or not password or not check_password: QMessageBox.warning(self.win, "警告","用户名和密码不能为空!") return if password == check_password: if password_check_sql(user):#验证账号是否存在 QMessageBox.critical(self.win,"错误","账号已经存在") else: res_logon(user,password) QMessageBox.information(self.win,"提示框","注册成功") #清空输入框 self.ui.lineEdit_3.clear() self.ui.lineEdit_4.clear() self.ui.lineEdit_5.clear() self.Z_change_D()#注册成功切换登录页面 else: QMessageBox.critical(self.win,"错误","两次密码不一样") class Home(QMainWindow): def __init__(self): super(Home, self).__init__() self.win=QMainWindow() self.ui=prc7.Ui_MainWindow() #主界面窗口对象 self.ui.setupUi(self.win) self.win.show() self.ui.pushButton.clicked.connect(self.change_groupbox) self.ui.pushButton_2.clicked.connect(self.change_groupbox_2) self.ui.pushButton_3.clicked.connect(self.change_groupbox_3) self.ui.pushButton_4.clicked.connect(self.change_groupbox_4) self.ui.pushButton_5.clicked.connect(self.change_groupbox_5) def change_groupbox(self): self.ui.groupBox.show() self.ui.groupBox_2.hide() self.ui.groupBox_3.hide() self.ui.groupBox_4.hide() self.ui.groupBox_5.hide() self.draw_1() def change_groupbox_2(self): self.ui.groupBox.hide() self.ui.groupBox_2.show() self.ui.groupBox_3.hide() self.ui.groupBox_4.hide() self.ui.groupBox_5.hide() self.draw_2() def change_groupbox_3(self): self.ui.groupBox.hide() self.ui.groupBox_2.hide() self.ui.groupBox_3.show() self.ui.groupBox_4.hide() self.ui.groupBox_5.hide() self.draw_3() def change_groupbox_4(self): self.ui.groupBox.hide() self.ui.groupBox_2.hide() self.ui.groupBox_3.hide() self.ui.groupBox_4.show() self.ui.groupBox_5.hide() self.draw_4() def change_groupbox_5(self): self.ui.groupBox.hide() self.ui.groupBox_2.hide() self.ui.groupBox_3.hide() self.ui.groupBox_4.hide() self.ui.groupBox_5.show() self.draw_5() def draw_1(self):#直方图 try: data=Db_R('rating_distribution') print("评分分布数据:", data) if data.empty: print("没有获取到评分分布数据") return x=list(data.iloc[:,0])#评分 y=list(data.iloc[:,1])#频次 F=MyFigure(5,4,100) axes=F.add_subplot(111) axes.bar(x,y,color="#ffffff") #设置坐标名称 axes.set_xlabel("频次", color="w") axes.set_ylabel("评分",color="w") F.fig.suptitle("贵阳旅游景点评分分析",color="w",fontsize=28)#标题 QGridLayout(self.ui.groupBox).addWidget(F) except Exception as e: print(f"绘制评分分布直方图时出错: {e}") def draw_2(self):#饼图 data = Db_R("price_level_distribution") labels = list(data.iloc[:, 0]) # 价格等级 sizes = list(data.iloc[:, 1]) # 数量 F = MyFigure(5, 4, 100) axes = F.add_subplot(111) axes.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90) axes.axis('equal') # 保证饼图是圆的 F.fig.suptitle("价格等级分析", color="w", fontsize=30) # 标题 QGridLayout(self.ui.groupBox_2).addWidget(F) def draw_3(self):#箱线图 data = Db_R("boxplot_statistics") F = MyFigure(5, 4, 100) axes = F.add_subplot(111) F.fig.suptitle("旅游景点评分和价格分析", color="w", fontsize=20) # 标题 QGridLayout(self.ui.groupBox_3).addWidget(F) def draw_4(self): # 散点图 data = Db_R('price_rating_relation') x = list(data.iloc[:, 0]) #价格 y = list(data.iloc[:, 1]) # 评分 F = MyFigure(5, 4, 100) axes = F.add_subplot(111) axes.scatter(x, y, color="#ffffff") # 设置坐标名称 axes.set_xlabel("价格(元)", color="w") axes.set_ylabel("评分", color="w") F.fig.suptitle("景点价格评分关系分析", color="w", fontsize=28) # 标题 QGridLayout(self.ui.groupBox_4).addWidget(F) def draw_5(self):#条形图 data=Db_R('top10_price') x=list(data.iloc[:,0])#价格 y=list(data.iloc[:,1])#景点名称 F=MyFigure(5,4,100) axes=F.add_subplot(111) axes.bar(x,y,color="#ffffff") #设置坐标名称 axes.set_xlabel("价格(元)", color="w") axes.set_ylabel("景点名称",color="w") F.fig.suptitle("贵阳景点价格TOP10排行榜",color="w",fontsize=28)#标题 QGridLayout(self.ui.groupBox_5).addWidget(F) if __name__=='__main__': QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)#做高清屏幕适配 app = QApplication(sys.argv) lyjd = Login_Register() lyjd.ui.pushButton_dl_2.clicked.connect(lyjd.D_change_z)#登录按钮链接注册切换登录函数,函数后括号不要 lyjd.ui.pushButton_dl_3.clicked.connect(lyjd.Z_change_D) lyjd.ui.pushButton_2.clicked.connect(lyjd.res_user) #注册 lyjd.ui.pushButton.clicked.connect(lyjd.login_user)#登录 lyjd.close_win() sys.exit(app.exec_())
最新发布
10-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值