PyQt: How to create a scrollable window

18.8k views Asked by At

I think it should be much easier to create a scrollable window in PyQt. I have a list of labels that goes out of the window and I would like to scroll down to view them. At the moment the code does not give me an error, but the window just doesn't appear:

class Example(QWidget):

    def __init__(self):
        super().__init__()

        layout = QVBoxLayout()

        lbl_arr = makeLabelArr()

        for i in range(1,8):
            qb = lbl_arr[i]
            # qb.setFixedWidth(300)
            layout.addWidget(qb)

        layout.setAlignment(Qt.AlignTop)

        scroll = QScrollArea()
        scroll.setWidget(self)
        scroll.setWidgetResizable(True)
        scroll.setFixedHeight(400)

        layout.addWidget(scroll)

        self.setLayout(layout)


        self.setGeometry(0, 0, 600, 220)
        self.setWindowTitle('SnP watchlist')

        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    #print(QDesktopWidget().availableGeometry())

    ex = Example()
    sys.exit(app.exec_())
4

There are 4 answers

3
ekhumoro On BEST ANSWER

Make the window itself a QScrollArea, like this:

class Window(QScrollArea):
    def __init__(self):
        super(Window, self).__init__()
        widget = QWidget()
        layout = QVBoxLayout(widget)
        layout.setAlignment(Qt.AlignTop)
        for index in range(100):
            layout.addWidget(QLabel('Label %02d' % index))
        self.setWidget(widget)
        self.setWidgetResizable(True)
1
Wasi On

You should set layout after adding the scroll bar widget.

class Example(QWidget):

    def __init__(self):
        super().__init__()

        layout = QVBoxLayout()

        lbl_arr = makeArrayOfLabelsHTML()

        for i in range(1,8):
            qb = lbl_arr[i]
            layout.addWidget(qb)

        layout.setAlignment(Qt.AlignTop)

        scroll = QScrollArea()
        scroll.setWidget(self)
        scroll.setWidgetResizable(True)
        scroll.setFixedHeight(400)
        layout.addWidget(scroll)

        # set layout after adding scroll bar 
        self.setLayout(layout)

        self.setGeometry(0, 0, 600, 220)
        self.setWindowTitle('SnP watchlist')

        self.show()



if __name__ == '__main__':

        app = QApplication(sys.argv)
        #print(QDesktopWidget().availableGeometry())

        ex = Example()
        sys.exit(app.exec_())
0
Mr Ed On

There is an example here: https://www.learnpyqt.com/tutorials/qscrollarea/

from PyQt5.QtWidgets import (QWidget, QSlider, QLineEdit, QLabel, QPushButton, QScrollArea,QApplication,
                             QHBoxLayout, QVBoxLayout, QMainWindow)
from PyQt5.QtCore import Qt, QSize
from PyQt5 import QtWidgets, uic
import sys


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.scroll = QScrollArea()             # Scroll Area which contains the widgets, set as the centralWidget
        self.widget = QWidget()                 # Widget that contains the collection of Vertical Box
        self.vbox = QVBoxLayout()               # The Vertical Box that contains the Horizontal Boxes of  labels and buttons

        for i in range(1,50):
            object = QLabel("TextLabel: "+str(i))
            self.vbox.addWidget(object)

        self.widget.setLayout(self.vbox)

        #Scroll Area Properties
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll.setWidgetResizable(True)
        self.scroll.setWidget(self.widget)

        self.setCentralWidget(self.scroll)

        self.setGeometry(600, 100, 1000, 900)
        self.setWindowTitle('Scroll Area Demonstration')
        self.show()

        return

def main():
    app = QtWidgets.QApplication(sys.argv)
    main = MainWindow()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
0
mike rodent On

This illustrates one way to do things

import sys 
from PyQt5 import QtWidgets, QtCore

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(600, 200)
        self.setWindowTitle('One way to do scrolling')
        
        def add_stuff():
            self.add_stuff()
        QtCore.QTimer.singleShot(500, add_stuff)

    def add_stuff(self):
        self.resize(1200, 800)
        
        # comment out these blocks, starting with the bottom one
        
        central_widget = QtWidgets.QFrame(self)
        self.setCentralWidget(central_widget)
        central_widget.setLayout(QtWidgets.QVBoxLayout(central_widget))
        central_widget.setStyleSheet('border: 5px solid green;')
        
        scroll_area = QtWidgets.QScrollArea()
        scroll_area.setWidgetResizable(True)
        central_widget.layout().addWidget(scroll_area)
        scroll_area.setStyleSheet('border: 5px solid blue;')
        
        main_panel = QtWidgets.QFrame(central_widget)
        main_panel.setStyleSheet('border: 5px solid red;')
        scroll_area.setWidget(main_panel)
        
        # the crucial line: without this, main_panel will just adapt
        # its size to its parent by default
        main_panel.setMinimumSize(600, 500)
        
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()        

Run this and then make the window smaller using your mouse.

It's also important to be aware that some components, such as QTableView and QTreeView, implement scrolling components automatically.

The main thing to understand is that the scrolling happens because the child becomes too big for its parent (and its parent has set up scrolling). setMinimumSize is a very unsubtle way to illustrate this constraint: normally the child of a scroll-enabled parent will contain certain subcomponents which at some point will have a size below which they can't be compressed... the configured layout object of the child will also play a role in determining the minimum dimensions of the child.