Нужно ли мне когда-нибудь вручную уничтожать объекты (например, растровые изображения)? - PullRequest
2 голосов
/ 08 мая 2020

Я пишу приложение на основе PySide2 , которое включает QScrollArea, в котором содержится много QPixmap изображений (или лучше: список QLabel, которые, в свою очередь, содержат растровые изображения). Этот список изображений со временем может стать довольно большим, поэтому по достижении определенного числа я периодически удаляю некоторые из этих изображений из области прокрутки, что отлично работает.

Однако у меня сложилось впечатление, что даже после удаления некоторых изображений потребление памяти моим приложением осталось прежним. Поэтому удаления виджетов меток может быть недостаточно. Из документов PySide2 на QLayout.removeWidget():

Удаляет виджет widget из макета. После этого вызова вызывающая сторона обязана придать виджету разумную геометрию или вернуть виджет в макет или явно скрыть его, если необходимо.

Чтобы удалить виджет, я делаю это следующее:

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

Итак, мой вопрос: нужно ли мне вручную уничтожать метки / изображения, которые я удалил из макета, или они должны быть автоматически собраны в мусор?

1 Ответ

2 голосов
/ 08 мая 2020

Чтобы понять операцию, вы должны иметь следующие четкие концепции:

  • QObject не будет удален G C, если у него есть родитель.
  • Когда виджет добавляется в макет, затем виджет устанавливается как дочерний для виджета, в котором был установлен макет.
  • При использовании 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()
...