【问题标题】:Do I ever need to manually destroy objects (like pixmaps)?我是否需要手动销毁对象(如像素图)?
【发布时间】:2020-05-08 14:15:48
【问题描述】:

我正在编写一个基于 PySide2 的应用程序,其中包括一个 QScrollArea,其中包含大量 QPixmap 图像(或更好:QLabel 的列表依次包含像素图)。该图像列表会随着时间的推移变得非常大,因此当达到一定数量时,我会定期从滚动区域中删除其中一些图像 - 这很好。

然而,我确实有这样的印象,即使在删除了一些图像之后,我的应用程序的内存消耗仍然相同。因此删除标签小部件可能还不够。来自 QLayout.removeWidget() 上的 PySide2 文档:

从布局中删除小部件小部件。在此调用之后,调用者有责任为小部件提供合理的几何形状,或者将小部件放回布局中,或者在必要时显式隐藏它。

为了删除小部件,我执行以下操作:

while self.images_scroll_layout.count() > MAX_IMAGES:
    to_remove = self.images_scroll_layout.itemAt(self.images_scroll_layout.count() - 1)
    self.images_scroll_layout.removeItem(to_remove)
    to_remove.widget().deleteLater()

所以我的问题是:我需要手动销毁从布局中删除的标签/像素图,还是应该自动将它们进行垃圾收集?

【问题讨论】:

    标签: python memory-management pyside2 qlabel qpixmap


    【解决方案1】:

    要理解操作你必须有以下清晰的概念:

    • 如果 QObject 有父对象,则 GC 不会删除它。
    • 将小部件添加到布局时,该小部件将设置为建立布局的小部件的子级。
    • 使用 removeWidget() 时,只会从处理布局的小部件列表中删除小部件,因此小部件的父级仍然是处理布局的小部件。

    为了验证您可以使用以下代码,其中不会发出指示 QObject 何时被删除的破坏信号。

    from PySide2 import QtWidgets
    
    
    class Widget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.add_button = QtWidgets.QPushButton(self.tr("Add"), clicked=self.add_widget)
            self.remove_button = QtWidgets.QPushButton(
                self.tr("Remove"), clicked=self.remove_widget
            )
    
            scrollarea = QtWidgets.QScrollArea(widgetResizable=True)
            widget = QtWidgets.QWidget()
            scrollarea.setWidget(widget)
    
            lay = QtWidgets.QVBoxLayout(self)
            lay.addWidget(self.add_button)
            lay.addWidget(self.remove_button)
            lay.addWidget(scrollarea)
    
            self.resize(640, 480)
    
            self.label_layouts = QtWidgets.QVBoxLayout(widget)
    
            self.counter = 0
    
        def add_widget(self):
            label = QtWidgets.QLabel(f"label {self.counter}")
            self.label_layouts.addWidget(label)
            self.counter += 1
    
        def remove_widget(self):
            item = self.label_layouts.itemAt(0)
            if item is None:
                return
            widget = item.widget()
            if widget is None:
                return
            widget.destroyed.connect(print)
            print(f"widget: {widget} Parent: {widget.parentWidget()}")
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())
    

    总结:removeWidget() 不用于从内存中删除小部件,而只会使布局不处理该小部件,如果要删除小部件,则必须使用 deleteLater()。

    def remove_widget(self):
        item = self.label_layouts.itemAt(0)
        if item is None:
            return
        widget = item.widget()
        if widget is None:
            return
        widget.destroyed.connect(print)
        widget.deleteLater()
    

    【讨论】:

    • 这很有帮助,非常感谢。因此,我将对已从布局中删除的小部件调用deleteLater()。不过还有一个问题:如前所述,我将QPixmap 放在QLabel 上,并且该标签被添加到滚动区域布局中。像素图没有deleteLater() 方法。仅在标签对象上调用该方法是否足够,还是我还需要以某种方式删除像素图?
    • @Matthias 当你删除 QLabel 时,你内存中的所有内容都会被删除,包括 QPixmap
    • 好的,现在当我在已删除的小部件上调用 deleteLater() 时,我收到以下错误:RuntimeError: Internal C++ object (PySide2.QtWidgets.QWidgetItem) already deleted.。有什么想法吗?
    • @Matthias 我认为在您的代码中某处您正在访问已被删除的小部件,如果没有minimal reproducible example,您将无法为您提供帮助。
    • @Matthias 更改为 for i in range(MAX, self.images_scroll_layout.count()): item = self.images_scroll_layout.itemAt(i) item.widget().deleteLater()
    猜你喜欢
    • 2020-04-06
    • 2011-10-20
    • 2021-06-05
    • 2010-12-31
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 2012-06-25
    相关资源
    最近更新 更多