Search code examples
pythonpyqt5qscrollarea

QScrollArea not showing scrollbars PyQt5 Python


I have been trying to create a program where QFrames are dynamically added to a QVboxLayout. As more and more frames are added to the layout, the frames not having enough space to occupy. I searched google for the problem, I found many stackoverflow answers, all of which use a QScrollArea. But when I added a QScrollArea with the QVBoxLayout in it, the scrollbars don't show up. Any help would be greatly appreciated. Here is the minimum reproducible example:

from PyQt5 import QtCore, QtGui, QtWidgets

class Frame(QtWidgets.QFrame):
    def __init__(self,parent=None):
        super(Frame,self).__init__(parent)
        self.setStyleSheet("background-color:red")
        self.lbl=QtWidgets.QLabel(self)
        self.lbl.setText("Sample Test")
        self.font=QtGui.QFont()
        self.font.setPointSize(20)
        self.lbl.setFont(self.font)
        
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        
        MainWindow.setObjectName("MainWindow")
        MainWindow.setFixedSize(984, 641)
        MainWindow.setStyleSheet("background-color:rgb(255,255,255);")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        #Some UI

        self.addBtn = QtWidgets.QPushButton(self.centralwidget)
        self.addBtn.setGeometry(QtCore.QRect(450, 100, 71, 51))
        self.addBtn.setText("+")
        font = QtGui.QFont()
        font.setPointSize(20)
        self.addBtn.setFont(font)
        self.addBtn.setStyleSheet("background-color:rgb(89, 183, 255);border-radius:15px;color:white;")
        self.addBtn.setFlat(True)
        self.addBtn.setObjectName("addTaskBtn")
        self.addBtn.clicked.connect(self.addFrame)
        
        self.scroller = QtWidgets.QScrollArea(self.centralwidget)
        self.scroller.setGeometry(QtCore.QRect(0, 230, 991, 411))
        self.scroller.setWidgetResizable(True)
        self.scroller.setObjectName("scroller")
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 989, 409))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.layoutManager=QtWidgets.QVBoxLayout(self.scroller)
        self.scrollAreaWidgetContents.setLayout(self.layoutManager)
        self.scroller.setWidget(self.scrollAreaWidgetContents)
        MainWindow.setCentralWidget(self.centralwidget)


    def addFrame(self):
        #Code to add the frame
        self.layoutManager.addWidget(Frame())

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Solution

  • The problem is that when you use the widgetResizable mode in True it uses the sizeHint of the widget but in your case the sizeHint of the QFrame does not take into account that of the QLabel. One possible solution is to use a layout.

    class Frame(QtWidgets.QFrame):
        def __init__(self, parent=None):
            super(Frame, self).__init__(parent)
            self.setStyleSheet("background-color:red")
    
            self.lbl = QtWidgets.QLabel()
            self.lbl.setText("Sample Test")
    
            font = QtGui.QFont()
            font.setPointSize(20)
            self.lbl.setFont(font)
    
            lay = QtWidgets.QVBoxLayout(self)
            lay.addWidget(self.lbl)
    

    On the other hand I see that in my test the scrollbar appears but only a part since as you established it using setGeometry it has problems to recalculate some values. A possible solution is to use a widget that contains the QScrollArea:

    # ...
    self.addBtn.clicked.connect(self.addFrame)
    
    container = QtWidgets.QWidget(self.centralwidget)
    container.setGeometry(QtCore.QRect(0, 230, 991, 411))
    lay_container = QtWidgets.QVBoxLayout(container)
    
    self.scroller = QtWidgets.QScrollArea()
    lay_container.addWidget(self.scroller)
    self.scroller.setWidgetResizable(True)
    # ...