title: PyQt - Oriented Flow Layout
body: <p>I'm trying to adapt <a href="https://github.com/baoboa/pyqt5/blob/master/examples/layouts/flowlayout.py" rel="nofollow noreferrer">this PyQt implementation of <code>FlowLayout</code></a> to allow vertical flow as well as horizontal. This is my current implementation:</p>
  
  <pre><code>from PyQt5.QtWidgets import *
  from PyQt5.QtGui import *
  from PyQt5.QtCore import *
  
  
  class FlowLayout(QLayout):
      def __init__(self, orientation=Qt.Horizontal, parent=None, margin=0, spacing=-1):
          super().__init__(parent)
          self.orientation = orientation
  
          if parent is not None:
              self.setContentsMargins(margin, margin, margin, margin)
  
          self.setSpacing(spacing)
  
          self.itemList = []
  
      def __del__(self):
          item = self.takeAt(0)
          while item:
              item = self.takeAt(0)
  
      def addItem(self, item):
          self.itemList.append(item)
  
      def count(self):
          return len(self.itemList)
  
      def itemAt(self, index):
          if index &gt;= 0 and index &lt; len(self.itemList):
              return self.itemList[index]
  
          return None
  
      def takeAt(self, index):
          if index &gt;= 0 and index &lt; len(self.itemList):
              return self.itemList.pop(index)
  
          return None
  
      def expandingDirections(self):
          return Qt.Orientations(Qt.Orientation(0))
  
      def hasHeightForWidth(self):
          return self.orientation == Qt.Horizontal
  
      def heightForWidth(self, width):
          return self.doLayout(QRect(0, 0, width, 0), True)
  
      def hasWidthForHeight(self):
          return self.orientation == Qt.Vertical
  
      def widthForHeight(self, height):
          return self.doLayout(QRect(0, 0, 0, height), True)
  
      def setGeometry(self, rect):
          super().setGeometry(rect)
          self.doLayout(rect, False)
  
      def sizeHint(self):
          return self.minimumSize()
  
      def minimumSize(self):
          size = QSize()
  
          for item in self.itemList:
              size = size.expandedTo(item.minimumSize())
  
          margin, _, _, _ = self.getContentsMargins()
  
          size += QSize(2 * margin, 2 * margin)
          return size
  
      def doLayout(self, rect, testOnly):
          x = rect.x()
          y = rect.y()
          offset = 0
          horizontal = self.orientation == Qt.Horizontal
  
          for item in self.itemList:
              wid = item.widget()
              spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
              spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
  
              if horizontal:
                  next = x + item.sizeHint().width() + spaceX
                  if next - spaceX &gt; rect.right() and offset &gt; 0:
                      x = rect.x()
                      y += offset + spaceY
                      next = x + item.sizeHint().width() + spaceX
                      offset = 0
              else:
                  next = y + item.sizeHint().height() + spaceY
                  if next - spaceY &gt; rect.bottom() and offset &gt; 0:
                      x += offset + spaceX
                      y = rect.y()
                      next = y + item.sizeHint().height() + spaceY
                      offset = 0
  
              if not testOnly:
                  item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
  
              if horizontal:
                  x = next
                  offset = max(offset, item.sizeHint().height())
              else:
                  y = next
                  offset = max(offset, item.sizeHint().width())
  
          return y + offset - rect.y() if horizontal else x + offset - rect.x()
  
  
  if __name__ == '__main__':
      class Window(QWidget):
          def __init__(self):
              super().__init__()
  
              #flowLayout = FlowLayout(orientation=Qt.Horizontal)
              flowLayout = FlowLayout(orientation=Qt.Vertical)
              flowLayout.addWidget(QPushButton("Short"))
              flowLayout.addWidget(QPushButton("Longer"))
              flowLayout.addWidget(QPushButton("Different text"))
              flowLayout.addWidget(QPushButton("More text"))
              flowLayout.addWidget(QPushButton("Even longer button text"))
              self.setLayout(flowLayout)
  
              self.setWindowTitle("Flow Layout")
  
      import sys
  
      app = QApplication(sys.argv)
      mainWin = Window()
      mainWin.show()
      sys.exit(app.exec_())
  </code></pre>
  
  <p>This implementation has 2 (likely related) problems when handling vertical layouts:</p>
  
  <ol>
  <li><code>QLayout</code> has the <code>hasHeightForWidth</code> and <code>heightForWidth</code> methods, but not their inverses <code>hasWidthForHeight</code> and <code>widthForHeight</code>. I implemented the latter two methods regardless, but I doubt they're ever actually getting called.</li>
  <li>When using the horizontal variant of the layout, the window is automatically appropriately sized to contain all the items. When using the vertical variant, this is not the case. However, the vertical layout does work properly if you manually resize the window.</li>
  </ol>
  
  <p>How do I properly implement a vertical flow layout?</p>