M
M
Maxim0452020-02-18 17:57:07
Python
Maxim045, 2020-02-18 17:57:07

What could be causing the unstable behavior of QListWidget(Python3, PyQt5)?

In the code I'll leave below, there are three classes - ListWidget, Label and MyWindow. ListWidget creates a rubber band and Label creates a QLabel widget with a rounded image. You should not pay attention to these classes, they definitely perform their functions correctly. Problem(s) in MyWindow class.

  1. Here I'm creating a simple_btn button, which then goes into the vertical container box2. While it does not perform and should not perform any functions.
  2. I create a listWidget - a copy of the ListWidget class, which I fill with QLable(label1, label2, label3) widgets obtained from the Label class.
  3. I create scrollArea(QScrollArea), but I don't place it anywhere yet.
  4. I create QGridLayout - box1, where I add listWidget as the first widget (0, 0).
  5. I bind the click() method of the MyWindow class to the label1 , label2 and label3 widgets (I pass the num argument to this method), which hides the listWidget using hide() and creates a QGridLayout bound to the scrollArea - box3, if it has not been created yet. box3 is filled with buttons, the number of which depends on the num argument. The back_btn button gets into the vertical container box2.
  6. I bind the on_button method to the back_btn button. This method should remove all widgets from the box3 container, hide the scrollArea with hide() , and make the listWidget back visible with show() .

The essence of the problem:
I clicked on one of the QLabels and using the back_btn button returned to the original position of the widgets. If you do this a number of times in a row, a strange bug may occur - the widgets inside the listWidget stop changing their position depending on the size of the window. There are few specifics in these two sentences, I know, but, as I understand it, there is no specific combination that can lead to a bug, sometimes it happens right away, sometimes it doesn’t happen at all. The bug appears quite often, I can not close my eyes to it. The same thing happens on the other computer. I can't imagine why, I really need help.
5e4bfa7bdb5e3715901932.png
5e4bfa86b36c6774570318.png
5e4bfa8fba0dc936406012.png
5e4bfa9ac2255479744624.png
The bug can happen with any widget arrangement:
5e4bfad1cf70b200131703.png
Here is the code:
from PyQt5 import QtCore, QtWidgets, QtGui

class ListWidget(QtWidgets.QListWidget):
    def __init__(self, *args, **kwargs):
        super(ListWidget, self).__init__(*args, **kwargs)

        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)

    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.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.simple_btn = QtWidgets.QPushButton('Button')
        self.simple_btn.setFixedSize(100, 60)

        self.listWidget = ListWidget()

        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)

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

    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.on_button) 
        self.back_btn.setFixedSize(100, 60)
        self.box2.addWidget(self.back_btn)

    def on_button(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_())

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question