# -*- 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_())