我是否需要手动销毁对象(如像素图)?

Do I ever need to manually destroy objects (like pixmaps)?

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

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

Removes the widget widget from the layout. After this call, it is the caller’s responsibility to give the widget a reasonable geometry or to put the widget back into a layout or to explicitly hide it if necessary.

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

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()

所以我的问题是:我是否需要手动销毁我从布局中删除的 labels/pixmaps,或者它们应该自动被垃圾收集?

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

  • 如果 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()