С чем может быть связано нестабильное поведение QListWidget?(Python3, PyQt5)

Я только что задавал вопрос касательно кода ниже. Но теперь у меня появилась еще одна проблема.

У меня есть три класса - ListWidget, Label и MyWindow. ListWidget создает резиновую ленту, а Label создает виджет QLabel с закругленным изображением. На эти классы не стоит обращать внимания, они точно правильно выполняют свои функции. Проблема(проблемы) в классе MyWindow.

  1. Тут я создаю кнопку simple_btn, которая потом попадает в вертикальный контейнер box2. Пока она не выполняет и не должна выполнять никаких функций.
  2. Создаю listWidget - копию класса ListWidget, который наполняю виджетами QLable(label1, label2, label3), полученными из класса Label.
  3. Создаю scrollArea(QScrollArea), но пока нигде не размещаю.
  4. Создаю QGridLayout - box1, куда первым виджетом(0, 0) добавляю listWidget.
  5. К виджетам label1 , label2 и label3 привязываю метод класса MyWindow - click()(при этом передаю этому методу аргумент num), который с помощью hide() скрывает listWidget и создает привязанный к scrollArea контейнер QGridLayout - box3, если он еще не был создан. box3 наполняется кнопками, число которых зависит от аргумента num. В вертикальный контейнер box2 попадает кнопка back_btn.
  6. К кнопке back_btn привязваю метод onButton. Этот метод должен удалять все виджеты контейнера box3, скрывать scrollArea с помощью hide() и делать обратно видимым listWidget с помощью show().

В чем проблема:

Если выполнить некоторую комбинацию действий несколько раз(нажать на какой-нибудь из виджетов QLabel, а потом вернуться обратно кнопкой back_btn), часто listWidget перестает нормально работать. Виджеты внутри него перестают менять свое пололожение в зависимости от размера окна. Это происходит достаточно спонтанно(чаще всего при нажатии на label3) и я не могу объяснить, что именно вызывает проблему. На другом компьютере происходит то же самое. введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

Вот правильное отображение виджетов:

введите сюда описание изображения

Баг происходит при любом положении виджетов:

введите сюда описание изображения

Честно говоря, я очень огорчен этим, и мне очень нужна помощь. Не могу представить, как это можно загуглить, куда копать. Пожалуйста, подскажите, что может приводить к этой ошибке?

from PyQt5 import QtCore, QtWidgets, QtGui


class ListWidget(QtWidgets.QListWidget):
    def __init__(self, *args, **kwargs):
        super(ListWidget, self).__init__(*args, **kwargs)
        #self.setWindowTitle('ListWidget')
        #self.setStyleSheet('border-style: hidden;')
        self.resize(520, 400)

        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

        self.setEditTriggers(self.NoEditTriggers)
        self.setDefaultDropAction(QtCore.Qt.IgnoreAction)
        self.setSelectionMode(self.ContiguousSelection)

        self.setFlow(self.LeftToRight)
        self.setWrapping(True)
        self.setResizeMode(self.Adjust)

        self.setSpacing(10)

        self._rubberPos  = None
        self._rubberBand = None #QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)

    def makeItem(self, lb):
        item = QtWidgets.QListWidgetItem(self)
        item.setSizeHint(QtCore.QSize(140, 140))
        self.setItemWidget(item, lb)


class Label(QtWidgets.QLabel):
    clicked = QtCore.pyqtSignal()

    def __init__(self, picture, *args, **kwargs):
        super(Label, self).__init__(*args, **kwargs)

        self.setMaximumSize(140, 140)
        self.setMinimumSize(140, 140)
        self.radius = 10 

        self.target = QtGui.QPixmap(self.size())  
        self.target.fill(QtCore.Qt.transparent)    

        p = QtGui.QPixmap(picture).scaled(140, 140, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation)

        painter = QtGui.QPainter(self.target)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True)
        painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True)

        path = QtGui.QPainterPath()
        path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
        painter.setClipPath(path)
        painter.drawPixmap(0, 0, p)
        self.setPixmap(self.target)

    def mouseReleaseEvent(self, event):
        self.clicked.emit()


class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.simple_btn  = QtWidgets.QPushButton('Button')
        self.simple_btn.setFixedSize(100, 60)

        self.listWidget = ListWidget()

        self.scrollArea = QtWidgets.QScrollArea()
        self.content_widget = QtWidgets.QWidget()
        self.scrollArea.setWidget(self.content_widget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

        self.label1 = Label('picture1.png')
        self.listWidget.makeItem(self.label1)
        self.label1.clicked.connect(lambda num=2: self.click(num))
        self.label2 = Label('picture1.png')
        self.listWidget.makeItem(self.label2)
        self.label2.clicked.connect(lambda num=3: self.click(num))
        self.label3 = Label('picture1.png')
        self.listWidget.makeItem(self.label3)
        self.label3.clicked.connect(lambda num=4: self.click(num))

        self.box2 = QtWidgets.QVBoxLayout()
        self.box2.addWidget(self.simple_btn, alignment=QtCore.Qt.AlignRight)

        self.box1 = QtWidgets.QGridLayout(self)                                     
        self.box1.addWidget(self.listWidget, 0, 0)
        self.box1.setColumnStretch(0, 1)
        self.box1.addLayout(self.box2, 0, 1) 

    def click(self, num):
        self.listWidget.hide()

        if not hasattr(self, 'box3'):
            self.box1.addWidget(self.scrollArea, 0, 0)
            self.box3 = QtWidgets.QGridLayout(self.content_widget)
        else:
            self.scrollArea.show()

        for n in range(1, num):
            btn = QtWidgets.QPushButton(f'Button{n}')
            self.box3.addWidget(btn, n-1, 0)

        self.back_btn = QtWidgets.QPushButton('Back', clicked=self.onButton) 
        self.back_btn.setFixedSize(100, 60)
        self.box2.addWidget(self.back_btn, alignment=QtCore.Qt.AlignRight)

    def onButton(self):
        self.back_btn.deleteLater()
        col = 0
        for row in range(self.box3.rowCount()):
            if self.box3.itemAtPosition(row, col) is not None:
                w = self.box3.itemAtPosition(row, col).widget()
            w.deleteLater()
        self.scrollArea.hide()
        self.listWidget.show()


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle(' ')
    window.show()
    sys.exit(app.exec_())

Ответы (1 шт):

Автор решения: S. Nick

я переписал только class MyWindow и отметил основные изменения.

Пробуйте.

from PyQt5 import QtCore, QtWidgets, QtGui


class ListWidget(QtWidgets.QListWidget):
    def __init__(self, *args, **kwargs):
        super(ListWidget, self).__init__(*args, **kwargs)
        #self.setWindowTitle('ListWidget')
        #self.setStyleSheet('border-style: hidden;')
        self.resize(520, 400)

        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

        self.setEditTriggers(self.NoEditTriggers)
        self.setDefaultDropAction(QtCore.Qt.IgnoreAction)
        self.setSelectionMode(self.ContiguousSelection)

        self.setFlow(self.LeftToRight)
        self.setWrapping(True)
        self.setResizeMode(self.Adjust)

        self.setSpacing(10)

        self._rubberPos  = None
        self._rubberBand = None #QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)

    def makeItem(self, lb):
        item = QtWidgets.QListWidgetItem(self)
        item.setSizeHint(QtCore.QSize(140, 140))
        self.setItemWidget(item, lb)


class Label(QtWidgets.QLabel):
    clicked = QtCore.pyqtSignal()

    def __init__(self, picture, *args, **kwargs):
        super(Label, self).__init__(*args, **kwargs)

        self.setMaximumSize(140, 140)
        self.setMinimumSize(140, 140)
        self.radius = 10 

        self.target = QtGui.QPixmap(self.size())  
        self.target.fill(QtCore.Qt.transparent)    

        p = QtGui.QPixmap(picture).scaled(140, 140, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation)

        painter = QtGui.QPainter(self.target)
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True)
        painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True)

        path = QtGui.QPainterPath()
        path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
        painter.setClipPath(path)
        painter.drawPixmap(0, 0, p)
        self.setPixmap(self.target)

    def mouseReleaseEvent(self, event):
        self.clicked.emit()


class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setMinimumSize(310, 190)                                         # +

        self.simple_btn  = QtWidgets.QPushButton('Button')
        self.simple_btn.setFixedSize(100, 60)

        listWidget = ListWidget()                                             # +

        self._listLabel = []                                                  # +!!!
        self.label1 = Label('head3.png')
        self.label1.clicked.connect(lambda num=2: self.click(num))
        self._listLabel.append(self.label1)
        self.label2 = Label('Ok.png')
        self.label2.clicked.connect(lambda num=3: self.click(num))
        self._listLabel.append(self.label2)
        self.label3 = Label('im.png')
        self.label3.clicked.connect(lambda num=4: self.click(num))
        self._listLabel.append(self.label3)

        for lb in self._listLabel:
            listWidget.makeItem(lb)        

        self.back_btn = QtWidgets.QPushButton('Back', clicked=self.onButton)   # +!!!
        self.back_btn.setFixedSize(100, 60)   

        self.box2 = QtWidgets.QVBoxLayout()
        self.box2.addWidget(self.simple_btn, alignment=QtCore.Qt.AlignRight)
        self.box2.addWidget(self.back_btn, alignment=QtCore.Qt.AlignRight)     # +++  
        self.back_btn.hide()                                                   # +++    

        self.box1 = QtWidgets.QGridLayout(self)                                     
        self.box1.addWidget(listWidget, 0, 0)
        self.box1.setColumnStretch(0, 1)
        self.box1.addLayout(self.box2, 0, 1) 

    def click(self, num):
        scrollArea = QtWidgets.QScrollArea()
        content_widget = QtWidgets.QWidget()
        scrollArea.setWidget(content_widget)
        scrollArea.setWidgetResizable(True)
        scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        box3 = QtWidgets.QGridLayout(content_widget)
        self.box1.addWidget(scrollArea, 0, 0)
        for n in range(1, num):
            btn = QtWidgets.QPushButton(f'Button{n}')
            box3.addWidget(btn, n-1, 0)
        self.back_btn.show()

    def onButton(self):
        self.back_btn.hide()

        listWidget = ListWidget()
        for lb in self._listLabel:
            listWidget.makeItem(lb) 
        self.box1.addWidget(listWidget, 0, 0)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle(' ')
    window.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка