龙空技术网

实战PyQt5: 079-如何彻底删除布局中一个部件

爱好史地的coder 126

前言:

此刻姐妹们对“python清空变量值”大致比较注重,咱们都想要学习一些“python清空变量值”的相关知识。那么小编也在网上搜集了一些关于“python清空变量值””的相关内容,希望姐妹们能喜欢,姐妹们一起来学习一下吧!

问题的提出

在Qt的GUI布局中,有时候需要彻底删除一个部件,释放其占用的内存,比如在一个网格布局中,在不影响布局的情况下,需要删除其中的某个部件,我们调用removeWidget()函数将其从布局中删除,这种操作下,虽然在界面上看不到了被移除的部件,但这并不说明,部件已经被删除,所占用的内存已经被释放。实际上,这些部件并没有被删除,占用的内存并没有释放。在Python里,可以采用以下三种方法来达到目的。

方法1:使用部件的deleteLater的函数

在Qt中,所有继承自QObject的类都有一个deleteLater函数,用于在适当的时候销毁对象自身。其原理是QObject.deleteLater()并不立即将对象销毁,而是向主循环发送了-个事件(event),在主循环收到这个事件后,让所有事件都发送完一切处理好后才销毁对象,释放相应的内存。这样做的好处是可以在这些延迟删除的时间内完成一些操作,而且就算调用多次的deletelater也是安全的。坏处就是内存释放会不及时。

方法2:使用sip.delete函数删除部件

导入sip库

import PyQt5.sip

使用其delete函数来删除部件对象

sip.delete(widget)

这种方法和C++中使用delete删除对象的原理是一样的。

这种方法的好处是,立即删除对象和释放内存,坏处是,可能会导致不可预知的崩溃现象。

Qt中不建议使用手动使用delete来释放QObject对象,原因有二:

不注意父子关系可能或导致某个对象释放两次,一次是手动释放,一次是parent释放。删除一个pending events等待传递的QObject会导致崩溃,所以不能直接跨线程删除对象,而QObject析构函数会断开所有信号和槽,因此用deleteLater代替比较好,它会让所有事件都发送完一切处理好后马上清除这片内存,而且就算调用多次的deletelater也是安全的。

如果没有上述两种原因存在,可以直接使用delete来删除对象和释放内存空间。

方法3:使用del函数

python提供了del 删除变量的方式,del 删除变量的原理是,它使变量的引用计数减一,如果引用计数为0,就会被回收。这样也可以达到目的,其使用方式同方法2,效果和方法2也是一样的。

测试代码

测试代码演示使用按钮,添加一些标签,可以选择用两种不同的方式来删除添加的标签,右边打印出操作后的信息,可以看到delteLater 函数并不马上执行删除并释放空间的操作,而sip.delete则马上执行删除操作并释放组件所占用的内存空间。完整测试代码如下:

import sysfrom PyQt5 import QtCore, QtGui, QtWidgetsfrom PyQt5 import sipfrom PyQt5.QtCore import Qtfrom PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,                              QGridLayout, QHBoxLayout, QVBoxLayout, QSplitter,                             QPushButton, QLabel, QPlainTextEdit) class DemoWidgetDelete(QMainWindow):    def __init__(self, parent=None):        super(DemoWidgetDelete, self).__init__(parent)                    # 设置窗口标题        self.setWindowTitle('实战PyQt5: 演示彻底删除部件')              # 设置窗口大小        self.resize(480, 300)              self.initUi()            def initUi(self):                mainLayout = QVBoxLayout()                       self.labelList = []                mainWidget = QWidget()        mainWidget.setLayout(mainLayout)        self.setCentralWidget(mainWidget)                #三个按钮        btnAdd = QPushButton('添加')        btnAdd.clicked.connect(self.onButtonAdd)        btnRemove1 = QPushButton('移除(deleteLater)')        btnRemove1.clicked.connect(self.onButtonRemove1)        btnRemove2 = QPushButton('移除(sip.delete)')        btnRemove2.clicked.connect(self.onButtonRemove2)        hLayout = QHBoxLayout()        hLayout.addWidget(btnAdd)        hLayout.addWidget(btnRemove1)        hLayout.addWidget(btnRemove2)                self.gLayout = QGridLayout()        self.gLayout.setSpacing(10)        widLeft = QWidget()        widLeft.setLayout(self.gLayout)                self.txtShow = QPlainTextEdit()        splitter = QSplitter()        splitter.addWidget(widLeft)        splitter.addWidget(self.txtShow)        splitter.addWidget(self.txtShow)        splitter.setStretchFactor(0, 1)         mainLayout.addLayout(hLayout)        mainLayout.addWidget(splitter)        #mainLayout.addStretch(Qt.Vertical)            def onButtonAdd(self):        addPos = self.gLayout.count() + 1        addNum = len(self.labelList)        self.labelList.append(QLabel('标签 ' + str(addPos)))        self.gLayout.addWidget(self.labelList[addNum], addPos, 0)        #方法1    def onButtonRemove1(self):        self.txtShow.clear()        self.txtShow.appendPlainText('deleteLater:')        for idx in range(self.gLayout.count()+1, 1, -1):            #print(self.gLayout.rowCount(), len(self.gLayout), self.gLayout.count())            self.txtShow.appendPlainText('{},{},{}'.format(self.gLayout.rowCount(), len(self.gLayout), self.gLayout.count()))                        wid = self.gLayout.itemAtPosition(idx-1, 0).widget()            wid.deleteLater()                    #尝试继续访问        for idx in range (self.gLayout.count()+1, 1, -1):            #print(idx, self.gLayout.itemAtPosition(idx - 1, 0).widget().size())            self.txtShow.appendPlainText('{},{}'.format(idx, self.gLayout.itemAtPosition(idx - 1, 0).widget().size()))        #方法2    def onButtonRemove2(self):        self.txtShow.clear()        self.txtShow.appendPlainText('sip.delete:')        for idx in range(self.gLayout.count()+1, 1, -1):            #print(self.gLayout.rowCount(), len(self.gLayout), self.gLayout.count())            self.txtShow.appendPlainText('{},{},{}'.format(self.gLayout.rowCount(), len(self.gLayout), self.gLayout.count()))                        wid = self.gLayout.itemAtPosition(idx-1, 0).widget()                 self.gLayout.removeWidget(wid)            sip.delete(wid)                           #尝试继续访问        for idx in range (self.gLayout.count()+1, 1, -1):            #print(idx, self.gLayout.itemAtPosition(idx - 1, 0).widget().size())            self.txtShow.appendPlainText('{},{}'.format(idx, self.gLayout.itemAtPosition(idx - 1, 0).widget().size()))        if __name__ == '__main__':    app = QApplication(sys.argv)    window = DemoWidgetDelete()    window.show()    sys.exit(app.exec())

运行结果如下图:

测试部件的多种删除方法

本文知识点彻底释放Qt对象的三种方法;deleteLater的优点和缺点;什么时候可以使用delete来删除部件,释放空间;使用del来删除部件。

喜欢本文内容就关注, 收藏,点赞,评论和转发。

标签: #python清空变量值