certflow.widgets.flow_layout 源代码
"""自动换行的 ``FlowLayout``。
根治「标签 + 控件」稀疏单控件被 ``addStretch()`` 撑满整行的问题(重构计划第 4/5 节)。
控件按内容宽度排布,一行放不下自动折到下一行。
"""
from __future__ import annotations
from PySide6.QtCore import QRect, QSize, Qt
from PySide6.QtWidgets import QLayout, QWidget
[文档]
class FlowLayout(QLayout):
"""流布局:水平排布子项,超出宽度自动换行。"""
def __init__(self, parent: QWidget | None = None, margin: int = 0, spacing: int = -1) -> None:
super().__init__(parent)
if parent is not None:
self.setContentsMargins(margin, margin, margin, margin)
self.setSpacing(spacing if spacing >= 0 else 6)
self._items: list[QLayout.Item] = []
[文档]
def addItem(self, item: QLayout.Item) -> None: # noqa: N802
self._items.append(item)
[文档]
def count(self) -> int: # noqa: N802
return len(self._items)
[文档]
def itemAt(self, index: int) -> QLayout.Item | None: # noqa: N802
return self._items[index] if 0 <= index < len(self._items) else None
[文档]
def takeAt(self, index: int) -> QLayout.Item | None: # noqa: N802
return self._items.pop(index) if 0 <= index < len(self._items) else None
[文档]
def expandingDirections(self) -> Qt.Orientations: # noqa: N802
return Qt.Orientation(0)
[文档]
def hasHeightForWidth(self) -> bool: # noqa: N802
return True
[文档]
def heightForWidth(self, width: int) -> int: # noqa: N802
return self._do_layout(QRect(0, 0, width, 0), True)
[文档]
def setGeometry(self, rect: QRect) -> None: # noqa: N802
super().setGeometry(rect)
self._do_layout(rect, False)
[文档]
def sizeHint(self) -> QSize: # noqa: N802
return self.minimumSize()
[文档]
def minimumSize(self) -> QSize: # noqa: N802
size = QSize()
for item in self._items:
size = size.expandedTo(item.minimumSize())
margins = self.contentsMargins()
return size + QSize(
2 * margins.left() + 2 * margins.right(),
2 * margins.top() + 2 * margins.bottom(),
)
def _do_layout(self, rect: QRect, test_only: bool) -> int:
margins = self.contentsMargins()
effective = rect.adjusted(
+margins.left(), +margins.top(), -margins.right(), -margins.bottom()
)
x = effective.x()
y = effective.y()
line_height = 0
space_x = self.spacing()
space_y = self.spacing()
for item in self._items:
w = item.sizeHint().width()
h = item.sizeHint().height()
next_x = x + w + space_x
if next_x - space_x > effective.right() and line_height > 0:
x = effective.x()
y += line_height + space_y
next_x = x + w + space_x
line_height = 0
if not test_only:
item.setGeometry(QRect(x, y, w, h))
x = next_x
line_height = max(line_height, h)
return y + line_height - rect.y() + margins.bottom()