New to Qt5 and Python? Check out our complete PyQt5 tutorial.

Add scrollable regions with QScrollArea

Run out of space in your GUI? Add a scrollable region to your application

When you start building apps that display long documents, large amounts of data or large numbers of widgets, it can be difficult to arrange things within a fixed-size window. Resizing the window beyond the size of the screen isn't an option, and shrinking widgets to fit can make the information unreadable.

To illustrate the problem below is a window in which we've created a large number of QLabel widgets. These widgets have the size Vertical Policy set to Preferred which automatically resizes the widgets down to fit the available space. The results are unreadable.

Problem of Too Many Widgets.png
Problem of Too Many Widgets.png

Settings the Vertical Policy to Fixed keeps the widgets at their natural size, making them readable again.

Problem of Too Many Widgets With Fixed Heights
Problem of Too Many Widgets With Fixed Heights

However, while we can still add as many labels as we like, eventually they start to fall off the bottom of the layout.

To solve this problem GUI applications can make use of scrolling regions to allow the user to move around within the bounds of the application window while keeping widgets at their usual size. By doing this an almost unlimited amount of data or widgets can be shown, navigated and viewed within a window — although care should be taken to make sure the result is still usable!

In this tutorial, we'll cover adding a scrolling region to your PyQt application using QScrollArea.

Adding a QScrollArea in Qt Designer

First we'll look at how to add a QScrollArea from Qt Designer.

from the standard empty app importing a .ui file designed in Qt Designer.

python
from PyQt5 import QtWidgets, uic
import sys

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        #Load the UI Page
        uic.loadUi('mainwindow.ui', self)


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

if __name__ == '__main__':
    main()
Qt Creator — Select MainWindow for widget type
Qt Creator — Select MainWindow for widget type

So we will choose the scroll area widget and add it to our layout as below.

First, create an empty MainWindow in Qt Designer and save it as mainwindow.ui

Add Scroll Area
Add Scroll Area

Next choose to lay out the QScrollArea vertically or horizontally, so that it scales with the window.

Lay Out The Scroll Area Vertically Or Horizontally
Lay Out The Scroll Area Vertically Or Horizontally

Voila, we now have a completed scroll area that we can populate with anything we need.

The Scroll Area Is Created
The Scroll Area Is Created

Inserting Widgets

We will now add labels to that scroll area. Lets take two labels and place it inside the QScrollArea. We will then proceed to right click inside the scroll area and select Lay Out Vertically so our labels will be stacked vertically.

Add Labels to The Scroll Area And Set the Layout
Add Labels to The Scroll Area And Set the Layout

We've set the background to blue so the illustration of this this works is clear. We can now add more labels to the QScrollArea and see what happens. By default, the Vertical Policy of the label is set to Preferred which means that the label size is adjusted according to the constraints of widgets above and below.

Next, we'll add a bunch of widgets.

Adding More Labels to QScrollArea
Adding More Labels to QScrollArea

Any widget can be added into a `QScrollArea` although some make more sense than others. For example, it's a great way to show multiple widgets containing data in a expansive dashboard, but less appropriate for control widgets — scrolling around to control an application can get frustrating.

Note that the scroll functionality has not been triggered, and no scrollbar has appeared on the right hand side. Instead the labels are still progressively getting smaller in height to accommodate the widgets.

However, if we set Vertical Policy to Fixed and set the minimumSize of height to 100px the labels will no longer be able to shrink vertically into the available space. As the layout overflows this will now trigger the QScrollArea to display a scrollbar.

Setting Fixed Heights for Labels
Setting Fixed Heights for Labels

With that, our scrollbar appears on the right hand side. What has happened is that the scroll area only appears when necessary. Without a fixed height constraint on the widget, Qt assumes the most logical way to handle the many widgets is to resize them. But by imposing size constraints on our widgets, the scroll bar appears to allow all widgets to keep their fixed sizes.

Another important thing to note is the properties of the scroll area. Instead of adjusting fixed heights, we can keep it in Preferred , we can set the properties of the verticalScrollBar to ScrollBarAlwaysOn which will enable the scroll bar to appear sooner as below

ScrollArea Properties
ScrollArea Properties

Saving and running the code at the start of this tutorial gives us this scroll area app which is what we wanted.

App With Scroll Bar
App With Scroll Bar

Adding a QScrollArea from code

As with all widgets you can also add a QScrollArea directly from code. Below we repeat the above example, with a flexible scroll area for a given number of widgets, using code.

python
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")
            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()

If you run the above code you should see the output below, with a custom widget repeated multiple times down the window, and navigable using the scrollbar on the right.

Scroll Area App
Scroll Area App

Next, we'll step through the code to explain how this view is constructed.

First we create our layout hierarchy. At the top level we have our QMainWindow which we can set the QScrollArea onto using .setCentralWidget. This places the QScrollArea in the window, taking up the entire area.

To add content to the QScrollArea we need to add a widget using .setWidget, in this case we are adding a custom QWidget onto which we have applied a QVBoxLayout containing multiple sub-widgets.

python
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")
            self.vbox.addWidget(object)

This gives us the following hierarchy in the window:

Scroll Area This is the scroll area, added as the centralWidget to the QMainWindow
Widget This is the placeholder widget onto which we've applied the Vertical Layout below
Vbox Vertical Layout containing all the QLabel widgets

Finally we set up properties on the QScrollArea, setting the vertical scrollbar Always On and the horizontal Always Off. We allow the widget to be resized, and then add the central placeholder widget to complete the layout.

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

Finally, we will add the QScrollArea as the central widget for our QMainWindow and set up the window dimensions, title and show the window.

python
self.setCentralWidget(self.scroll)

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

Conclusion.

In this tutorial we've learned how to add a scrollbar with an unlimited number of widgets, programatically or using Qt Designer. Adding a QScrollArea is a good way to include multiple widgets especially on apps that are data intensize and require objects to be displayed as lists.

Have a go at making your own apps with QScrollArea and share with us what you have made!

For more information about using QScrollArea check out the PyQt5 documentation.

Enjoyed this?
Then you will enjoy my book!

The hands-on guide to making desktop apps with Python. Build real apps — learn more than just the basics. Updated 2019.

Check it out

Discussion