Skip to content
  • 0 Votes
    1 Posts
    399 Views
    No one has replied
  • 0 Votes
    10 Posts
    2k Views
    I

    Ohhh i thought so about implementing my own flow layout. Tried to avoid this, because this will be difficult to do, but if there is no other way...

    Anyway, thank you. You helped me to understand a little graphics view framework and saved a lot of time.
    Best regards!

  • 0 Votes
    2 Posts
    2k Views
    Stefan ScherfkeS

    The solution was (surprisingly) simple: Use the FlowLayout’s heightChanged signal to update the minimum height of the container (the ScrollArea’s widget).

    Here is a working example:

    """ PyQt5 port of the `layouts/flowlayout <https://doc.qt.io/qt-5/qtwidgets-layouts-flowlayout-example.html>`_ example from Qt5. """ from PyQt5.QtCore import pyqtSignal, QPoint, QRect, QSize, Qt from PyQt5.QtWidgets import QLayout, QSizePolicy, QSpacerItem class FlowLayout(QLayout): """A ``QLayout`` that aranges its child widgets horizontally and vertically. If enough horizontal space is available, it looks like an ``HBoxLayout``, but if enough space is lacking, it automatically wraps its children into multiple rows. """ heightChanged = pyqtSignal(int) def __init__(self, parent=None, margin=0, spacing=-1): super().__init__(parent) if parent is not None: self.setContentsMargins(margin, margin, margin, margin) self.setSpacing(spacing) self._item_list = [] def __del__(self): while self.count(): self.takeAt(0) def addItem(self, item): # pylint: disable=invalid-name self._item_list.append(item) def addSpacing(self, size): # pylint: disable=invalid-name self.addItem(QSpacerItem(size, 0, QSizePolicy.Fixed, QSizePolicy.Minimum)) def count(self): return len(self._item_list) def itemAt(self, index): # pylint: disable=invalid-name if 0 <= index < len(self._item_list): return self._item_list[index] return None def takeAt(self, index): # pylint: disable=invalid-name if 0 <= index < len(self._item_list): return self._item_list.pop(index) return None def expandingDirections(self): # pylint: disable=invalid-name,no-self-use return Qt.Orientations(Qt.Orientation(0)) def hasHeightForWidth(self): # pylint: disable=invalid-name,no-self-use return True def heightForWidth(self, width): # pylint: disable=invalid-name height = self._do_layout(QRect(0, 0, width, 0), True) return height def setGeometry(self, rect): # pylint: disable=invalid-name super().setGeometry(rect) self._do_layout(rect, False) def sizeHint(self): # pylint: disable=invalid-name return self.minimumSize() def minimumSize(self): # pylint: disable=invalid-name size = QSize() for item in self._item_list: minsize = item.minimumSize() extent = item.geometry().bottomRight() size = size.expandedTo(QSize(minsize.width(), extent.y())) margin = self.contentsMargins().left() size += QSize(2 * margin, 2 * margin) return size def _do_layout(self, rect, test_only=False): m = self.contentsMargins() effective_rect = rect.adjusted(+m.left(), +m.top(), -m.right(), -m.bottom()) x = effective_rect.x() y = effective_rect.y() line_height = 0 for item in self._item_list: wid = item.widget() space_x = self.spacing() space_y = self.spacing() if wid is not None: space_x += wid.style().layoutSpacing( QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal) space_y += wid.style().layoutSpacing( QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical) next_x = x + item.sizeHint().width() + space_x if next_x - space_x > effective_rect.right() and line_height > 0: x = effective_rect.x() y = y + line_height + space_y next_x = x + item.sizeHint().width() + space_x line_height = 0 if not test_only: item.setGeometry(QRect(QPoint(x, y), item.sizeHint())) x = next_x line_height = max(line_height, item.sizeHint().height()) new_height = y + line_height - rect.y() self.heightChanged.emit(new_height) return new_height if __name__ == '__main__': import sys from PyQt5.QtWidgets import QApplication, QPushButton, QScrollArea, QVBoxLayout, QWidget, QGroupBox app = QApplication(sys.argv) container = QWidget() container_layout = QVBoxLayout() for i in range(2): g = QGroupBox(f'Group {i}') l = FlowLayout(margin=10) l.heightChanged.connect(container.setMinimumHeight) g.setLayout(l) l.addWidget(QPushButton('Short')) l.addWidget(QPushButton('Longer')) l.addWidget(QPushButton('Different text')) l.addWidget(QPushButton('More text')) l.addWidget(QPushButton('Even longer button text')) container_layout.addWidget(g) container_layout.addStretch() container.setLayout(container_layout) w = QScrollArea() w.setWindowTitle('Flow Layout') w.setWidgetResizable(True) w.setWidget(container) w.show() sys.exit(app.exec_())