Почему неправильно отображается QScrollArea?(PyQt5, Python3)

У меня есть код, который я оставлю ниже.

Класс Label принимает путь к изображению и закругляет его края. Из-за некоторых обстоятельств буду использовать его экземпляры вместо кнопок.

В классе MyWindow я создаю два QGridLayout'а - box1 и box3 и один QVBoxLayout - box2:

  1. В box2 добавляю кнопку button.
  2. Создаю QScrollArea(scrollArea) и привязываю ее к box3
  3. box3 наполняю двумя виджетами QLabel, которые получаю из класса Label.
  4. box2 и scrollArea помещаю в box1.

Все это может показаться лишними, но мне действительно нужно такое количество контейнеров. Этого не будет видно в оставленном мною коде, т.к. перед созданием вопроса я пытаюсь упростить ситуацию, чтобы было проще искать ошибки.

Сейчас у меня есть минимум две проблемы.

Первая проблема:

Почему-то QScrollArea отказывается принимать нормальные размеры. Даже если растянусь окно на весь экран, элементы box3(а их всего два) не будут видны полностью. Полосы прокрутки остаются на своих местах, хотя виджетам хватило бы места.

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

Если добавить третий QLabel:

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

Вторая проблема: Я пытаюсь применить метод setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) к scrollArea. Но, почему-то это работает совсем не так, как мне хотелось - пропадают обе полосы прокрутки(горизонтальная и вертикальная). Я не знаю, в чем дело.

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

Пожалуйста, подскажите, как мне починить код?

from PyQt5 import QtCore, QtWidgets, QtGui

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

        self.label1 = Label('picture1.png')
        self.label1.clicked.connect(self.click)
        self.label2 = Label('picture1.png')

        self.new_button = QtWidgets.QPushButton('New Button')

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

        scrollArea = QtWidgets.QScrollArea()
        scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) #<--- ПРИВОДИТ К НЕПРАВИЛЬНОМУ ОТОБРАЖЕНИЮ
        content_widget = QtWidgets.QWidget()
        scrollArea.setWidget(content_widget)
        scrollArea.setWidgetResizable(True)
        self.box3 = QtWidgets.QGridLayout(content_widget)                           # QGridLayout
        self.box3.setSpacing(200)
        self.box3.addWidget(self.label1, 0, 0)
        self.box3.addWidget(self.label2, 0, 1) 

        self.box1 = QtWidgets.QGridLayout(self)                                     # QGridLayout
        self.box1.addWidget(scrollArea, 0, 0, 1, 1, QtCore.Qt.AlignCenter)
        self.box1.setColumnStretch(0, 1)
        self.box1.addLayout(self.box2, 0, 1)

    def click(self):
        for row in range(self.box3.rowCount()):
            for col in range(self.box3.columnCount()):
                w = self.box3.itemAtPosition(row, col).widget()
                w.deleteLater()
        self.box3.addWidget(self.new_button, 0, 0, 1, 1, QtCore.Qt.AlignCenter)


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

UPD: Я хочу, чтобы контейнер QGridLayout в QScrollArea занимал все свободное пространство окна, которое не занимает вертикальный контейнер box2. Еще на картинке виджеты контейнера box3 перескакивают на другую строчку, если им не хватает места. Понимаю, что наглею, но, если это будет не трудно, расскажите, как можно добиться такого? Чтобы было понятнее нарисовал схему:

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


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

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

То, что вы хотите сделать - Резиновая лента.

для этого нам нужен - class ListWidget(QListWidget):.

также посмотрите в class MyWindow(QtWidgets.QWidget): где задействована self.listWidget

Нажимайте на кнопку Button, изменяйте размер окна.

from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.Qt import *


class ListWidget(QListWidget):
    def __init__(self, *args, **kwargs):
        super(ListWidget, self).__init__(*args, **kwargs)
        self.setWindowTitle('ListWidget')
        self.resize(520, 400)
        # Скрыть горизонтальную полосу прокрутки
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        # Невозможно отредактировать
        self.setEditTriggers(self.NoEditTriggers)
        self.setDefaultDropAction(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 = QRubberBand(QRubberBand.Rectangle, self)

    def makeItem(self, lb):
        item = QListWidgetItem(self)
        item.setSizeHint(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.button = QtWidgets.QPushButton('Button', clicked=self.onButton) 
        self.button.setFixedSize(60, 60)

        self.listWidget = ListWidget()                                         # +++

        self.label1 = Label('head3.png')
        self.listWidget.makeItem(self.label1)                                  # +++ ...
        self.label1.clicked.connect(self.click)
        self.label2 = Label('Ok.png')
        self.listWidget.makeItem(self.label2)
        self.label3 = Label('head3.png')
        self.listWidget.makeItem(self.label3)

        self.new_button = QtWidgets.QPushButton('New Button')

        self.box2 = QtWidgets.QVBoxLayout()                                                
        self.box2.addWidget(self.button, 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):
        self.listWidget.hide()
        self.box1.addWidget(self.new_button, 0, 0, 1, 1, QtCore.Qt.AlignCenter)
        self.new_button.show()

    def onButton(self):
        self.new_button.hide()
        self.listWidget.show()
        label = Label('im.png')
        self.listWidget.makeItem(label)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle('Резиновая лента')
    window.show()
    sys.exit(app.exec_())

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

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

→ Ссылка