pythonpyqt6qscrollareaqgridlayout

How to adapt a QscrollArea to correctly display a QgridLayout that is changing inside?


I would like to create a bunch of QcheckBox with a QgridLayout inside a QscrollArea, and I need to modify the number of Checkbox of the QGridLayout.

My issue is when I increase the number of check boxes, they are compressed inside the QscrollArea instead of creating a scrollBar at the right side.

Here is my simple example:

import sys
import numpy as np
from PyQt6.QtGui import *
from PyQt6.QtCore import *
from PyQt6.QtWidgets import *


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__()
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.mainHBOX = QVBoxLayout()

        layout_toApply = QGroupBox('Checkboxes')
        self.layout_toApply_V = QVBoxLayout()
        self.grid_toApply_V = QGridLayout()
        self.Stimulation_NbSources_Edit =QLineEdit('100')
        self.Stimulation_NbSources_PB =QPushButton('Apply')
        self.ChangeNBSource_fun()
        layout_toApply.setLayout(self.layout_toApply_V)
        self.layout_toApply_V.addLayout(self.grid_toApply_V)

        self.layout_toApply_V.addWidget(self.Stimulation_NbSources_Edit)
        self.layout_toApply_V.addWidget(self.Stimulation_NbSources_PB)

        self.mainHBOX.addWidget(layout_toApply)

        self.mainHBOX_W = QWidget()
        self.mainHBOX_W.setLayout(self.mainHBOX)


        self.widgetparam_w = QWidget()
        layout_toApplyparam_w = QHBoxLayout()
        self.widgetparam_w.setLayout(layout_toApplyparam_w)
        self.scrolltoparam_w = QScrollArea(self.widgetparam_w)
        self.scrolltoparam_w.setWidget(self.mainHBOX_W)

        self.mainlay = QHBoxLayout()
        self.mainlay.addWidget(self.scrolltoparam_w)



        self.centralWidget.setLayout(self.mainlay)
        self.centralWidget.setFixedSize(QSize(500,500))
        self.Stimulation_NbSources_PB.clicked.connect(self.ChangeNBSource_fun)


    def ChangeNBSource_fun(self):
        for i in reversed(range(self.grid_toApply_V.count())):
            widgetToRemove = self.grid_toApply_V.itemAt(i).widget()
            widgetToRemove.setParent(None)
            widgetToRemove.deleteLater()


        N = int(self.Stimulation_NbSources_Edit.text())
        nb_column = 10
        nb_line = int(np.ceil(N / nb_column))


        self.Apply_to_pop = []
        for l in np.arange(nb_line):
            for c in np.arange(nb_column):
                idx = (l) * nb_column + c + 1
                if idx <= N:
                    CB = QCheckBox(str(idx - 1))
                    CB.setFixedWidth(int(30))
                    CB.setChecked(False)
                    self.grid_toApply_V.addWidget(CB, l, c)
                    self.Apply_to_pop.append(CB)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

Here's when I launch the code :

enter image description here

And here's what I get when I double the number of Check Boxes: enter image description here


Solution

  • The QCheckBox widgets get compressed because QScrollArea's widget doesn't have a layout that can grow in size beyond QScrollArea's visible boundaries.

    You should set the grid layout on a widget and then set it to be the child of QScrollArea. Make sure that QScrollArea has QSizePolicy.Expanding to allow it to expand.

    Also set the resizing policy of QScrollArea to QScrollArea.SetWidgetResizable(True)

    import sys
    import numpy as np
    from PyQt6.QtGui import *
    from PyQt6.QtCore import QSize
    from PyQt6.QtWidgets import (
        QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
        QGridLayout, QGroupBox, QLineEdit, QPushButton, QCheckBox, QScrollArea
    )
    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__()
            self.centralWidget = QWidget()
            self.setCentralWidget(self.centralWidget)
            self.mainHBOX = QVBoxLayout()
            layout_toApply = QGroupBox('Checkboxes')
            self.layout_toApply_V = QVBoxLayout()
            self.grid_toApply_V = QGridLayout()
            self.Stimulation_NbSources_Edit = QLineEdit('100')
            self.Stimulation_NbSources_PB = QPushButton('Apply')
            self.scrollContent = QWidget()
            self.scrollContent.setLayout(self.grid_toApply_V)
            self.scrollArea = QScrollArea()
            self.scrollArea.setWidgetResizable(True)
            self.scrollArea.setWidget(self.scrollContent)
            layout_toApply.setLayout(self.layout_toApply_V)
            self.layout_toApply_V.addWidget(self.scrollArea)
            self.layout_toApply_V.addWidget(self.Stimulation_NbSources_Edit)
            self.layout_toApply_V.addWidget(self.Stimulation_NbSources_PB)
            self.mainHBOX.addWidget(layout_toApply)
            self.centralWidget.setLayout(self.mainHBOX)
            self.centralWidget.setFixedSize(QSize(500, 500))   
            self.Stimulation_NbSources_PB.clicked.connect(self.ChangeNBSource_fun)
            self.ChangeNBSource_fun()
        def ChangeNBSource_fun(self):
            for i in reversed(range(self.grid_toApply_V.count())):
                widgetToRemove = self.grid_toApply_V.itemAt(i).widget()
                if widgetToRemove:
                    widgetToRemove.setParent(None)
                    widgetToRemove.deleteLater()
            N = int(self.Stimulation_NbSources_Edit.text())
            nb_column = 10
            nb_line = int(np.ceil(N / nb_column))
            self.Apply_to_pop = []
            for l in range(nb_line):
                for c in range(nb_column):
                    idx = l * nb_column + c + 1
                    if idx <= N:
                        CB = QCheckBox(str(idx - 1))
                        CB.setFixedWidth(30)
                        CB.setChecked(False)
                        self.grid_toApply_V.addWidget(CB, l, c)
                        self.Apply_to_pop.append(CB)
            self.scrollContent.setMinimumSize(nb_column * 30, nb_line * 30)
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec())
    

    correct