Search code examples
pythonpyside2flowlayoutqlayout

Qt FlowLayout example - how to get sizeHint to be called when layout changes?


I want to arrange a bunch of QPushButtons in a layout so they will wrap horizontally (like text). I'm trying to use the Qt example FlowLayout in PySide2.

I've posted a simple example revision 2 here

(This is based on the official example here, which is why I'm so perturbed it doesn't work well.)

Specifically, it works well when the window is large enough:

window works well when large wide window works well when large tall

but doesn't prevent the window from being shrunk down small, whereas a VBoxLayout or GridLayout would:

window can be resized too small

I think the minimum size should depend on the current width. This should be a minimum size:

wide minimum size

and so should this:

narrow minimum size is tall

How can I get the layout to prevent its parent from being shrunk so small that its contents aren't visible?

sizeHint() and minimumSize() always return the size of a single item, which I thought was the problem. However if I rewrite them to take into account all items at the current layout width, it doesn't matter, because both are only called once, when I create and populate the layout.

My best idea so far is to call update() from heightForWidth(), roughly this:

def heightForWidth(self, width):
    oldWidth = self.geometry().width() - 2 * self.contentsMargins().top()
    oldHeight = self.doLayout(QtCore.QRect(0, 0, oldWidth, 0), True)
    height = self.doLayout(QtCore.QRect(0, 0, width, 0), True)
    print 'heightForWidth', width, oldWidth, height, oldHeight
    if height != oldHeight:
        self.update()
    return height

full sample with changes to minimumSize and doLayout is revision 3. It works sometimes, but has several problems:

  1. When the layout is first created there are constant calls to update(). I think the call to update() triggers a call to heightForWidth() and for some reason the width passed in is not close to the "oldWidth" that I calculate until I resize the window, even by a single pixel.

  2. As soon as I resize the window, calls to heightForWidth() stop completely, and minimumSize() stops being called, and I can no longer resize my window smaller than the last size calculated. I've no idea why.

  3. (Much less important) self.geometry().width() - 2 * self.contentsMargins().top() does not return the width that was last passed to heightForWidth (even when top/bottom/left/right margins are identical), though I took it from other code in the example. This doesn't usually matter but at certain widths it causes a flurry of calls to update()

I suppose I'm doing it all wrong. Can it be done right?


Solution

  • I've ported the original/official example to PySide2 now, and it works without changes. I'm not sure what was preventing it from working before.