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.
Settings the Vertical Policy to Fixed keeps the widgets at their natural size, making them readable again.
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
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.
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()
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
Next choose to lay out the
QScrollArea vertically or horizontally, so that it scales with the window.
Voila, we now have a completed scroll area that we can populate with anything we need.
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.
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.
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.
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
ScrollBarAlwaysOn which will enable the scroll bar to appear sooner as below
Saving and running the code at the start of this tutorial gives us this scroll area app which is what we wanted.
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.
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.
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.
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.
#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.
self.setCentralWidget(self.scroll) self.setGeometry(600, 100, 1000, 900) self.setWindowTitle('Scroll Area Demonstration') self.show()
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!