fereria
7/30/2021 - 5:38 PM

LayoutSample

# -*- coding: utf-8 -*-
# 参考C++コード
# https://stackoverflow.com/questions/55820951/qt-flowlayout-example-how-to-get-sizehint-to-be-called-when-layout-changes
# https://code.qt.io/cgit/qt/qtbase.git/tree/examples/widgets/layouts/flowlayout/flowlayout.cpp?h=5.15

import sys
import os.path


from PySide2.QtWidgets import (QApplication,
                               QDialog,
                               QLayout,
                               QPushButton,
                               QLayoutItem, QScrollArea,
                               QStyle,
                               QSizePolicy,
                               QVBoxLayout,
                               QWidget)
from PySide2.QtCore import (QSize, QMargins, QRect, Qt, QPoint)
from PySide2.QtUiTools import QUiLoader

CURRENT_PATH = os.path.dirname(os.path.abspath(sys.argv[0]))


class SampleGridLayout(QLayout):
    def __init__(self, margin: int, hSpacing: int, vSpacing: int, parent=None):
        super().__init__(parent=parent)
        self.setContentsMargins(margin, margin, margin, margin)

        self.itemList = []
        self.hSpacing = hSpacing
        self.vSpacing = vSpacing

    def addItem(self, item: QLayoutItem):

        self.itemList.append(item)

    def count(self):

        return len(self.itemList)

    def itemAt(self, index: int):

        if self.count() > 0 and index < self.count():
            return self.itemList[index - 1]
        return None

    def takeAt(self, index):

        if index >= 0 and index < self.count():
            return self.itemList.pop(index - 1)
        return None

    # def expandingDirections(self):
    #     return Qt.Orientation.Vertical

    def hasHeightForWidth(self):
        return True

    def heightForWidth(self, width: int):
        height = self.doLayout(QRect(0, 0, width, 0))
        return height

    def sizeHint(self):
        return self.minimumSize()

    def minimumSize(self):

        size = QSize()
        for item in self.itemList:
            size = size.expandedTo(item.minimumSize())

        margins = self.contentsMargins()
        size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom())

    def setGeomertry(self, rect):
        # 配置されるWidgetのジオメトリを計算するときに呼び出される
        super().setGeometry(rect)
        self.doLayout(rect)

    def doLayout(self, rect):

        # rect は、現在のLayoutの矩形
        x = rect.x()
        y = rect.y()
        lineHeight = 0

        for item in self.itemList:
            wid = item.widget()
            # 次のWidgetの配置場所
            nextX = x + item.sizeHint().width() + self.hSpacing
            if nextX - self.hSpacing > rect.right() and lineHeight > 0:
                x = rect.x()
                y = y + lineHeight + self.vSpacing
                nextX = x + item.sizeHint().width() + self.hSpacing
                lineHeight = 0

            item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
            x = nextX
            lineHeight = max(lineHeight, item.sizeHint().height())

        return y + lineHeight - rect.y()


class SampleUI(QDialog):

    def __init__(self, parent=None):
        super().__init__(parent)

        self.ui = QUiLoader().load(f"{CURRENT_PATH}/scroll.ui")

        layout = QVBoxLayout(self)
        self.setLayout(layout)
        layout.addWidget(self.ui)

        self.layout = SampleGridLayout(3, 5, 5)
        widget = self.ui.scrollArea.widget()
        widget.setLayout(self.layout)

        for i in range(100):
            btn = QPushButton(f"Sample {i}")
            self.layout.addWidget(btn)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    QApplication.setFallbackSessionManagementEnabled(True)
    a = SampleUI()
    a.show()
    sys.exit(app.exec_())