154 lines
4.8 KiB
Python
154 lines
4.8 KiB
Python
from typing import Optional, List, overload
|
|
|
|
from PyQt5.QtCore import Qt, QRect, QSize, QPoint
|
|
from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLayoutItem, QWidget
|
|
|
|
|
|
class FlowLayout(QLayout):
|
|
def __init__(self, parent=None):
|
|
super(FlowLayout, self).__init__(parent)
|
|
self.setObjectName(type(self).__name__)
|
|
self._hspacing = -1
|
|
self._vspacing = -1
|
|
self._items: List[QLayoutItem] = []
|
|
|
|
def __del__(self):
|
|
del self._items[:]
|
|
|
|
@overload
|
|
def indexOf(self, a0: QWidget) -> int:
|
|
try:
|
|
return next(idx for idx, item in enumerate(self._items) if item.widget() is a0)
|
|
except:
|
|
return -1
|
|
|
|
def indexOf(self, a0: QLayoutItem) -> int:
|
|
try:
|
|
return self._items.index(a0)
|
|
except:
|
|
return -1
|
|
|
|
def addItem(self, a0: QLayoutItem) -> None:
|
|
self._items.append(a0)
|
|
self.invalidate()
|
|
|
|
def removeItem(self, a0: QLayoutItem) -> None:
|
|
self._items.remove(a0)
|
|
self.invalidate()
|
|
|
|
def spacing(self) -> int:
|
|
hspacing = self.horizontalSpacing()
|
|
if hspacing == self.verticalSpacing():
|
|
return hspacing
|
|
else:
|
|
return -1
|
|
|
|
def setSpacing(self, a0: int) -> None:
|
|
self._hspacing = self._vspacing = a0
|
|
self.invalidate()
|
|
|
|
def setHorizontalSpacing(self, a0: int) -> None:
|
|
self._hspacing = a0
|
|
self.invalidate()
|
|
|
|
def horizontalSpacing(self):
|
|
if self._hspacing >= 0:
|
|
return self._hspacing
|
|
else:
|
|
return self.smartSpacing(QStyle.PM_LayoutHorizontalSpacing)
|
|
|
|
def setVerticalSpacing(self, a0: int) -> None:
|
|
self._vspacing = a0
|
|
self.invalidate()
|
|
|
|
def verticalSpacing(self):
|
|
if self._vspacing >= 0:
|
|
return self._vspacing
|
|
else:
|
|
return self.smartSpacing(QStyle.PM_LayoutVerticalSpacing)
|
|
|
|
def count(self) -> int:
|
|
return len(self._items)
|
|
|
|
def itemAt(self, index: int) -> Optional[QLayoutItem]:
|
|
if 0 <= index < len(self._items):
|
|
return self._items[index]
|
|
return None
|
|
|
|
def takeAt(self, index: int) -> Optional[QLayoutItem]:
|
|
if 0 <= index < len(self._items):
|
|
item = self._items.pop(index)
|
|
self.invalidate()
|
|
return item
|
|
return None
|
|
|
|
def expandingDirections(self) -> Qt.Orientations:
|
|
return Qt.Orientations(Qt.Orientation(0))
|
|
# return Qt.Horizontal | Qt.Vertical
|
|
|
|
def hasHeightForWidth(self) -> bool:
|
|
return True
|
|
|
|
def heightForWidth(self, a0: int) -> int:
|
|
return self.doLayout(QRect(0, 0, a0, 0), True)
|
|
|
|
def setGeometry(self, a0: QRect) -> None:
|
|
super(FlowLayout, self).setGeometry(a0)
|
|
self.doLayout(a0, False)
|
|
|
|
def sizeHint(self) -> QSize:
|
|
return QSize(
|
|
self.parent().contentsRect().size().width(),
|
|
self.minimumSize().height()
|
|
)
|
|
|
|
def minimumSize(self) -> QSize:
|
|
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
|
|
if not self._items:
|
|
return y + lineheight - rect.y() + bottom
|
|
for item in self._items:
|
|
if item.isEmpty():
|
|
continue
|
|
widget = item.widget()
|
|
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()
|