PySide6从0开始学习的笔记(二十五) Qt窗口对象的生命周期和及时销毁

问题的提出

先看一段代码:

python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QPushButton, QErrorMessage

app = QApplication(sys.argv)
# 创建一个消息框
msg_box = QErrorMessage()
msg_box.showMessage("消息框")

# 再次显示消息框
def show_message_box():
    try:
        msg_box.show()
    except RuntimeError as e:
        print(e)
    msg_box.show()

btn = QPushButton("显示消息框")
btn.clicked.connect(show_message_box)
btn.show()
sys.exit(app.exec())

运行结果:

正如之前学习过的,当Qt的窗口对象被关闭(close())之后,它并没有在内存中消失,只是暂时隐藏起来不显示,当再次执行show()就会显示。对于一些短时间运行的简单小应用,这种重复生成并且不被销毁的对象,不会对系统资源有多大的消耗,但如果是一个7*24h运行,频繁操作的应用,比如工业视觉检测、scada项目等,就不得不重视对象的生命周期,对于不再有用的对象要及时销毁,以免对系统资源带来浪费。

比如:

python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QPushButton, QErrorMessage

app = QApplication(sys.argv)
all_msg_box = []

for i in range(1, 11):
    msg_box = QErrorMessage()
    msg_box.showMessage(f"消息框: {i}")
    all_msg_box.append(msg_box)
    msg_box.close()

# 再次显示消息框
def show_message_box():
    try:
        for msg_box in all_msg_box:
            msg_box.show()
    except RuntimeError as e:
        print(e)

btn = QPushButton("显示消息框")
btn.clicked.connect(show_message_box)
btn.show()
sys.exit(app.exec())

所有生成的窗口都不会被销毁,长时间运行就会存在大量的这种垃圾。

解决方法

使用deleteLater自动销毁
python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QPushButton, QErrorMessage, QWidget, QVBoxLayout

app = QApplication(sys.argv)
all_msg_box = []

class Widget(QWidget):
    def __init__(self):
        super().__init__()
        btn = QPushButton("关闭本窗口", self)
        btn.clicked.connect(self.close)
        btn.clicked.connect(self.deleteLater)
        layout = QVBoxLayout(self)
        layout.addWidget(btn)


# 再次显示窗口
def show_window():
    try:
        widget.show()
    except RuntimeError as e:
        print(e)

widget = Widget()
widget.show()
x = widget.x()
y = widget.y()
btn = QPushButton("显示窗口")
btn.move(x+150, y)
btn.clicked.connect(show_window)
btn.show()
sys.exit(app.exec())

上面的代码,当点击"关闭本窗口按钮",按钮的点击信号连接了deleteLater()函数将窗口销毁。但是,只有点击"关闭本窗口按钮"关闭窗口,才能销毁,如果使用窗口右上角自带的关闭按钮关闭窗口,仍然不会被销毁。

为此,可以改写窗口部件的closeEvent()函数,将销毁事件加到窗口的关闭事件响应中:

python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout

app = QApplication(sys.argv)
all_msg_box = []

class Widget(QWidget):
    def __init__(self):
        super().__init__()
        btn = QPushButton("关闭本窗口", self)
        btn.clicked.connect(self.close)
        layout = QVBoxLayout(self)
        layout.addWidget(btn)

    def closeEvent(self, event):
        super().closeEvent(event)
        self.deleteLater()


# 再次显示窗口
def show_window():
    try:
        widget.show()
    except RuntimeError as e:
        print(e)

widget = Widget()
widget.show()
x = widget.x()
y = widget.y()
btn = QPushButton("显示窗口")
btn.move(x+150, y)
btn.clicked.connect(show_window)
btn.show()
sys.exit(app.exec())

无论采用哪种方式关闭窗口,都会销毁。
2.

设置WA_DeleteOnClose属性
python 复制代码
import sys

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QPushButton, QErrorMessage

app = QApplication(sys.argv)
# 创建一个消息框
msg_box = QErrorMessage()
msg_box.showMessage("消息框")
# 设置消息框关闭时自动删除
msg_box.setAttribute(Qt.WA_DeleteOnClose)

# 再次显示消息框
def show_message_box():
    try:
        msg_box.show()
    except RuntimeError as e:
        print(e)

btn = QPushButton("显示消息框")
btn.clicked.connect(show_message_box)
btn.show()
sys.exit(app.exec())

设置了WA_DeleteOnClose属性后,窗口关闭后就被自动销毁,与deleteLater()功能相同。
3.

利用父子关系自动销毁
python 复制代码
import sys

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QErrorMessage

app = QApplication(sys.argv)
# 再次显示消息框
def show_message_box():
    try:
        msg_box.show()
    except RuntimeError as e:
        print(e)

widget = QWidget()
widget.setFixedSize(100, 400)
widget.setAttribute(Qt.WA_DeleteOnClose)  # 设置窗口属性为WA_DeleteOnClose
widget.show()
msg_box = QErrorMessage(widget)  # 创建消息框对象,并将widget作为父窗口,随父窗口的销毁而销毁
msg_box.showMessage("消息框")
x = widget.x()
y = widget.y()
btn = QPushButton("显示消息窗口")
btn.move(x+150, y)
btn.clicked.connect(show_message_box)
btn.show()
sys.exit(app.exec())

当父对象设置了自动销毁,比如父对象是一个临时窗口,关闭时会随父对象一起自动销毁。
4.

使用静态对象
python 复制代码
import sys
from PySide6.QtWidgets import QApplication, QMessageBox

app = QApplication(sys.argv)

QMessageBox.warning(None, "警告", "文件未保存!")
app.quit()

sys.exit()

这种静态对象也是很常用的一个临时窗口创建方式。

  • 需要知道的是,这种窗口是以exec()方式阻塞运行的:
python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QMessageBox, QPushButton

app = QApplication(sys.argv)
btn = QPushButton("关闭程序")
btn.clicked.connect(app.quit)
btn.show()
QMessageBox.warning(None, "警告", "文件未保存!")
sys.exit(app.exec())

当窗口弹出时就无法操作别的窗口部件了。


除了Qt窗口部件,还有其他一些需要及时销毁的临时对象,比如:QThread线程、QTimer定时器、堆上创建的C++内存对象等。

及时销毁的本质原因是避免内存泄漏、资源占用和程序异常

  • 内存泄漏 :C++ 是手动内存管理语言,Qt 的非窗口对象(如QThread)若未显式销毁,其占用的堆内存不会被自动回收,长期运行会导致程序内存占用持续升高,最终可能崩溃。
  • 资源占用:线程、文件句柄、网络连接等对象不仅占用内存,还会占用系统资源(如 CPU 核心、端口),不销毁会导致系统资源耗尽,影响程序甚至系统稳定性。
  • 逻辑异常:比如未销毁的线程可能持续执行无效逻辑、占用锁资源,或导致重复创建线程引发冲突;悬空的 C++ 对象引用还可能触发野指针访问,直接导致程序崩溃。
相关推荐
癫狂的兔子14 小时前
【Python】【机器学习】贝叶斯算法
python·机器学习
代码小书生14 小时前
pillow,一个实用的 Python 库!
开发语言·python·pillow
A懿轩A14 小时前
【Java 基础编程】Java 异常处理保姆级教程:try-catch-finally、throw/throws、自定义异常
java·开发语言·python
追求源于热爱!14 小时前
记10,Gradio介绍
python
枷锁—sha15 小时前
【CTFshow-pwn系列】03_栈溢出【pwn 050】详解:动态链接下的 mprotect 与 ROP 链艺术
网络·笔记·安全·网络安全
破晓之翼15 小时前
Skill原理及国内大模型实践
人工智能·python
IT管理圈15 小时前
Cursor Rules 实战指南—让AI按你的规矩写代码
python
Java后端的Ai之路15 小时前
微调模型成本太高,用RAG技术,低成本实现AI升级
开发语言·人工智能·python·rag·ai升级
喵手15 小时前
Python爬虫实战:从零构建书籍价格情报数据库(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·csv导出·构建书籍价格情报·书籍价格采集
Asher阿舍技术站15 小时前
【AI基础学习系列】八、机器学习常见名词汇总
人工智能·学习·机器学习·常见名词