Created
September 30, 2022 18:22
-
-
Save rafalstapinski/496ce41724e19f4c513e4a810dbafb00 to your computer and use it in GitHub Desktop.
A custom Qt FlowLayout that efficiently reorganizes elements when the viewport changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
from PySide6 import QtWidgets, QtCore | |
class FlowLayout(QtWidgets.QLayout): | |
item_list: list[QtWidgets.QLayoutItem] | |
horizontal_space: int | |
vertical_space: int | |
margin: int | |
current_size: tuple[int, int] | |
item_width: int | |
item_height: int | |
def __init__(self, parent: QtWidgets.QWidget, item_width: int, item_height: int): | |
super().__init__(parent) | |
if parent is not None: | |
self.setContentsMargins(0, 0, 0, 0) | |
# spaces between each item | |
self.horizontal_space = 5 | |
self.vertical_space = 5 | |
self.item_width = item_width | |
self.item_height = item_height | |
self.item_list = [] | |
self.current_size = (0, 0) | |
def __del__(self): | |
item = self.takeAt(0) | |
while item: | |
item = self.takeAt(0) | |
def addItem(self, item): | |
self.item_list.append(item) | |
def count(self) -> int: | |
return len(self.item_list) | |
def itemAt(self, index) -> QtWidgets.QLayoutItem | None: | |
if index >= 0 and index < len(self.item_list): | |
return self.item_list[index] | |
return None | |
def takeAt(self, index: int) -> QtWidgets.QLayoutItem | None: | |
if index >= 0 and index < len(self.item_list): | |
return self.item_list.pop(index) | |
return None | |
def addWidget(self, widget: QtWidgets.QWidget): | |
super().addWidget(widget) | |
def expandingDirections(self) -> QtCore.Qt.Orientations: | |
return QtCore.Qt.Orientations(QtCore.Qt.Orientation(0)) | |
def hasHeightForWidth(self) -> bool: | |
return True | |
def heightForWidth(self, width: int) -> int: | |
if width == 0: | |
return -1 | |
# TODO take into account the right most horizontal_space | |
column_count = max([width // self.item_width, 1]) | |
row_count = math.ceil(len(self.item_list) / column_count) | |
height = row_count * (self.item_height + self.horizontal_space) | |
# if even, remove last spacing | |
if len(self.item_list) % 2 == 0: | |
height -= self.vertical_space | |
return height | |
def setGeometry(self, rect: QtCore.QRect): | |
super().setGeometry(rect) | |
self.doLayout(rect) | |
def sizeHint(self) -> int: | |
return self.minimumSize() | |
def minimumSize(self) -> int: | |
size = QtCore.QSize() | |
for item in self.item_list: | |
size = size.expandedTo(item.minimumSize()) | |
return size | |
def doLayout(self, rect: QtCore.QRect): | |
if (rect.width(), rect.height()) == self.current_size: | |
return | |
self.current_size = (rect.width(), rect.height()) | |
column_count = max([rect.width() // self.item_width, 1]) | |
centering_offset = (rect.width() - (column_count * self.item_width)) // 2 | |
row = 0 | |
column = 0 | |
for item in self.item_list: | |
x_offset = column * self.item_width + centering_offset | |
y_offset = row * self.item_height | |
column += 1 | |
if column == column_count: | |
column = 0 | |
row += 1 | |
item.setGeometry(QtCore.QRect(x_offset, y_offset, self.item_width, self.item_height)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment