Search code examples
pythonpyqtpyqt5qgridlayout

QGridlLayout with non-stretchable-height rows


I'm trying to build a QGridLayout with stretchable-width columns but non-stretchable-height rows. The grid in inside a QScrollArea, and with the exception of the height, it's almost working. You can see it in the following images:

enter image description here

enter image description here

As you can see, the rows are being vertically stretched. I would like all the rows to be equal and to not fit all the parent's height if there are too few rows (first image). Should I touch the grid or the actual widgets?

Edit: reproducible example

import sys
from PyQt5.QtWidgets import (QWidget, QGridLayout, QLabel, QRadioButton, QApplication, QScrollArea, QVBoxLayout)

class ScrollableGrid(QWidget):

    def __init__(self, columnSpans, minimumColumnWidth):

        super().__init__()

        # Grid
        self.grid = QWidget()
        self.gridLayout = QGridLayout()

        for i in range(len(columnSpans)):
            self.gridLayout.setColumnStretch(i, columnSpans[i])
            self.gridLayout.setColumnMinimumWidth(i, columnSpans[i] * minimumColumnWidth)

        self.grid.setLayout(self.gridLayout)

        # Scroll area
        self.scrollArea = QScrollArea()
        self.scrollArea.setWidget(self.grid)
        self.scrollArea.setWidgetResizable(True)

        # Compute the correct minimum width
        width = (self.grid.sizeHint().width() +
                 self.scrollArea.verticalScrollBar().sizeHint().width() +
                 self.scrollArea.frameWidth() * 2)

        self.scrollArea.setMinimumWidth(width)

        # Layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.scrollArea)
        self.setLayout(self.layout)


    def addRow(self, row, elements):
        for column in range(len(elements)):
            self.gridLayout.addWidget(elements[column], row, column)


class MainWindow(QWidget):

    def __init__(self):

        super().__init__()

        # ScrollableGrid
        self.grid = ScrollableGrid(columnSpans=[1,2,3], minimumColumnWidth=100)

        # Add rows
        for i in range(3):
            self.grid.addRow(i, [QLabel('A'), QLabel('B'), QRadioButton()])

        # Window layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.grid)
        self.setLayout(self.layout)


if __name__ == '__main__':

    app = QApplication(sys.argv)
    windowExample = MainWindow()
    windowExample.show()
    sys.exit(app.exec_())

Solution

  • void QGridLayout::setRowStretch(int row, int stretch)

    Sets the stretch factor of row row to stretch. The first row is number 0.

    The stretch factor is relative to the other rows in this grid. Rows with a higher stretch factor take more of the available space.

    Yes, is it because it must me called after all rows have been added.

    import sys
    from PyQt5.QtWidgets import (QWidget, QGridLayout, QLabel, QRadioButton, 
                                 QApplication, QScrollArea, QVBoxLayout)
    
    class ScrollableGrid(QWidget):
        def __init__(self, columnSpans, minimumColumnWidth):
            super().__init__()
    
            # Grid
            self.grid = QWidget()
            self.gridLayout = QGridLayout()
    
            for i in range(len(columnSpans)):
                self.gridLayout.setColumnStretch(i, columnSpans[i])
                self.gridLayout.setColumnMinimumWidth(i, columnSpans[i] * minimumColumnWidth)
            self.grid.setLayout(self.gridLayout)
    
            # Scroll area
            self.scrollArea = QScrollArea()
            self.scrollArea.setWidget(self.grid)
            self.scrollArea.setWidgetResizable(True)
    
            # Compute the correct minimum width
            width = (self.grid.sizeHint().width() +
                     self.scrollArea.verticalScrollBar().sizeHint().width() +
                     self.scrollArea.frameWidth() * 2)
            self.scrollArea.setMinimumWidth(width)
    
            # Layout
            self.layout = QVBoxLayout()
            self.layout.addWidget(self.scrollArea)
            self.setLayout(self.layout)
    
        def addRow(self, row, elements):
            for column in range(len(elements)):
                self.gridLayout.addWidget(elements[column], row, column)
            
    
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
    
            # ScrollableGrid
            self.grid = ScrollableGrid(columnSpans=[1,2,3], minimumColumnWidth=100)
    
            # Add rows
            for i in range(3):
                self.grid.addRow(i, [QLabel('A'), QLabel('B'), QRadioButton()])
                
            self.grid.gridLayout.setRowStretch(111, 1)                                # !!!
    
            # Window layout
            self.layout = QVBoxLayout()
            self.layout.addWidget(self.grid)
            self.setLayout(self.layout)
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        windowExample = MainWindow()
        windowExample.show()
        sys.exit(app.exec_())
    

    enter image description here