2023-01-31 00:59:47 +13:00
|
|
|
from typing import Optional
|
|
|
|
|
2022-06-22 11:38:04 +12:00
|
|
|
from PyQt5.QtCore import (
|
|
|
|
Qt,
|
|
|
|
QRect,
|
|
|
|
QSize,
|
|
|
|
QPoint,
|
|
|
|
)
|
|
|
|
from PyQt5.QtWidgets import (
|
|
|
|
QLayout,
|
|
|
|
QStyle,
|
|
|
|
QSizePolicy,
|
2023-01-31 00:59:47 +13:00
|
|
|
QLayoutItem,
|
2022-06-22 11:38:04 +12:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class FlowLayout(QLayout):
|
|
|
|
def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1):
|
|
|
|
super(FlowLayout, self).__init__(parent)
|
|
|
|
self._hspacing = hspacing
|
|
|
|
self._vspacing = vspacing
|
|
|
|
self._items = []
|
|
|
|
self.setContentsMargins(margin, margin, margin, margin)
|
|
|
|
self.setObjectName(type(self).__name__)
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
del self._items[:]
|
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def addItem(self, a0: QLayoutItem) -> None:
|
|
|
|
self._items.append(a0)
|
2022-06-22 11:38:04 +12:00
|
|
|
|
|
|
|
def horizontalSpacing(self):
|
|
|
|
if self._hspacing >= 0:
|
|
|
|
return self._hspacing
|
|
|
|
else:
|
|
|
|
return self.smartSpacing(QStyle.PM_LayoutHorizontalSpacing)
|
|
|
|
|
|
|
|
def verticalSpacing(self):
|
|
|
|
if self._vspacing >= 0:
|
|
|
|
return self._vspacing
|
|
|
|
else:
|
|
|
|
return self.smartSpacing(QStyle.PM_LayoutVerticalSpacing)
|
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def count(self) -> int:
|
2022-06-22 11:38:04 +12:00
|
|
|
return len(self._items)
|
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def itemAt(self, index: int) -> Optional[QLayoutItem]:
|
2022-06-22 11:38:04 +12:00
|
|
|
if 0 <= index < len(self._items):
|
|
|
|
return self._items[index]
|
2023-01-31 00:59:47 +13:00
|
|
|
return None
|
2022-06-22 11:38:04 +12:00
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def takeAt(self, index: int) -> Optional[QLayoutItem]:
|
2022-06-22 11:38:04 +12:00
|
|
|
if 0 <= index < len(self._items):
|
|
|
|
return self._items.pop(index)
|
2023-01-31 00:59:47 +13:00
|
|
|
return None
|
2022-06-22 11:38:04 +12:00
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def expandingDirections(self) -> Qt.Orientations:
|
2023-04-02 06:29:30 +12:00
|
|
|
return Qt.Horizontal | Qt.Vertical
|
2022-06-22 11:38:04 +12:00
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def hasHeightForWidth(self) -> bool:
|
2022-06-22 11:38:04 +12:00
|
|
|
return True
|
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def heightForWidth(self, a0: int) -> int:
|
|
|
|
return self.doLayout(QRect(0, 0, a0, 0), True)
|
2022-06-22 11:38:04 +12:00
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def setGeometry(self, a0: QRect) -> None:
|
|
|
|
super(FlowLayout, self).setGeometry(a0)
|
|
|
|
self.doLayout(a0, False)
|
2022-06-22 11:38:04 +12:00
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def sizeHint(self) -> QSize:
|
2023-04-01 10:54:39 +13:00
|
|
|
return QSize(
|
|
|
|
self.parent().contentsRect().size().width(),
|
|
|
|
self.minimumSize().height()
|
|
|
|
)
|
2022-06-22 11:38:04 +12:00
|
|
|
|
2023-01-31 00:59:47 +13:00
|
|
|
def minimumSize(self) -> QSize:
|
2022-06-22 11:38:04 +12:00
|
|
|
size = QSize()
|
|
|
|
for item in self._items:
|
|
|
|
size = size.expandedTo(item.minimumSize())
|
|
|
|
left, top, right, bottom = self.getContentsMargins()
|
|
|
|
size += QSize(left + right, top + bottom)
|
|
|
|
return size
|
|
|
|
|
|
|
|
def doLayout(self, rect, testonly):
|
|
|
|
left, top, right, bottom = self.getContentsMargins()
|
|
|
|
effective = rect.adjusted(+left, +top, -right, -bottom)
|
|
|
|
x = effective.x()
|
|
|
|
y = effective.y()
|
|
|
|
lineheight = 0
|
2023-04-02 06:29:30 +12:00
|
|
|
if not self._items:
|
|
|
|
return y + lineheight - rect.y() + bottom
|
2022-06-22 11:38:04 +12:00
|
|
|
for item in self._items:
|
2023-04-02 06:29:30 +12:00
|
|
|
if item.isEmpty():
|
|
|
|
continue
|
2022-06-22 11:38:04 +12:00
|
|
|
widget = item.widget()
|
|
|
|
if not widget.isVisible():
|
|
|
|
continue
|
|
|
|
hspace = self.horizontalSpacing()
|
|
|
|
if hspace == -1:
|
|
|
|
hspace = widget.style().layoutSpacing(
|
|
|
|
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal
|
|
|
|
)
|
|
|
|
vspace = self.verticalSpacing()
|
|
|
|
if vspace == -1:
|
|
|
|
vspace = widget.style().layoutSpacing(
|
|
|
|
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical
|
|
|
|
)
|
|
|
|
nextX = x + item.sizeHint().width() + hspace
|
|
|
|
if nextX - hspace > effective.right() and lineheight > 0:
|
|
|
|
x = effective.x()
|
|
|
|
y = y + lineheight + vspace
|
|
|
|
nextX = x + item.sizeHint().width() + hspace
|
|
|
|
lineheight = 0
|
|
|
|
if not testonly:
|
|
|
|
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
|
|
|
x = nextX
|
|
|
|
lineheight = max(lineheight, item.sizeHint().height())
|
|
|
|
return y + lineheight - rect.y() + bottom
|
|
|
|
|
|
|
|
def smartSpacing(self, pm):
|
|
|
|
parent = self.parent()
|
|
|
|
if parent is None:
|
|
|
|
return -1
|
|
|
|
elif parent.isWidgetType():
|
|
|
|
return parent.style().pixelMetric(pm, None, parent)
|
|
|
|
else:
|
|
|
|
return parent.spacing()
|