Search code examples
pythonspacingpyqt6qboxlayout

How to get stretch factor for QSpacerItems


When you addStretch() or insertStretch() to a QBoxLayout, a QSpacerItem is added/inserted to the layout and you can set the stretch factor while doing so.

Now I would like to retrieve that stretch factor from the SpacerItem but I can't find how to do so.
item.sizePolicy().verticalStretch() always returns 0, as can be demonstrated with this example:

from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QSpacerItem


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(700, 500, 100, 200)
        self.layout = QVBoxLayout(self)

        for i in range(3):
            but = QPushButton(f'Push {i}')
            self.layout.addWidget(but)
            if i == 0:
                but.clicked.connect(self.click)

        self.layout.insertStretch(1, 2)
        self.layout.insertStretch(3, 3)

    def click(self):
        for i in (1, 3):
            stretch = self.layout.itemAt(i)
            assert isinstance(stretch, QSpacerItem)
            pol = stretch.sizePolicy()
            print(pol.verticalStretch())


app = QApplication([])
window = Window()
window.show()
app.exec()

So where is the stretch factor stored? and is this info accessible somehow or would I have to keep a separate reference to keep the stretch info available?


Solution

  • tl;dr

    Use QBoxLayout.stretch(index).

    Explanation

    While the QSizePolicy does provide means to set its horizontal and vertical stretch factors, those are extended aspects that are normally considered only for widget items.

    It is up to the layout manager to eventually consider those stretch factors, which are almost considered as "hints".

    In reality, the basic layout managers (box and grid) keep their own private references to item stretch factors, eventually considering that of child widgets.

    This is clear from the fact that QSpacerItem does not provide neither stretch values in its constructor, nor a setSizePolicy() function. This means that its internal size policy is only used at its basic level (horizontal/vertical policy enums), without any way to eventually set or get their stretch value.

    For instance, QGridLayout provides setColumnStretch() and setRowStretch() functions (and related getters), which will eventually overrule any possible stretch factor that would be otherwise explicitly set/got from an inner/nested widget that has its own size policy with an explicit stretch factor.

    For boxed layouts, we have to remember that QVBoxLayout is a convenience subclass, just like its QHBoxLayout sibling; it actually is syntactic sugar for QBoxLayout with an implicit QBoxLayout.direction: it always is top-to-bottom for vertical layouts, and either left-to-right or right-to-left for horizontal ones, depending on the layoutDirection of the application, or that the managed widget (eventually considering explicit values set at any level in the widget tree).

    So, similarly to the columnStretch(col) and rowStretch(row) getter functions of QGridLayout, QBoxLayout provides a stretch(index) function, referring to the index of the layout item in the box ordering.

    That's the function you need to use.