PyQt6 Widgets

Using QPushButton, QCheckBox, QComboBox, QLabel and QSlider widgets

PyQt6 Tutorial Getting started with PyQt6

Heads up! You've already completed this tutorial.

In Qt (and most User Interfaces), widget is the name given to a component of the UI that the user can interact with. User interfaces are made up of multiple widgets, arranged within the window.

Qt comes with a large selection of widgets and even allows you to create your own custom widgets or customize existing ones.

A Quick Demo

First, let's have a look at some of the most common PyQt widgets. The following code creates a range of PyQt widgets and adds them to a window layout so you can see them together:

python
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QDateEdit,
    QDateTimeEdit,
    QDial,
    QDoubleSpinBox,
    QFontComboBox,
    QLabel,
    QLCDNumber,
    QLineEdit,
    QMainWindow,
    QProgressBar,
    QPushButton,
    QRadioButton,
    QSlider,
    QSpinBox,
    QTimeEdit,
    QVBoxLayout,
    QWidget,
)

# Subclass QMainWindow to customize your application's main window
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Widgets App")

        layout = QVBoxLayout()
        widgets = [
            QCheckBox,
            QComboBox,
            QDateEdit,
            QDateTimeEdit,
            QDial,
            QDoubleSpinBox,
            QFontComboBox,
            QLCDNumber,
            QLabel,
            QLineEdit,
            QProgressBar,
            QPushButton,
            QRadioButton,
            QSlider,
            QSpinBox,
            QTimeEdit,
        ]

        for w in widgets:
            layout.addWidget(w())

        widget = QWidget()
        widget.setLayout(layout)

        # Set the central widget of the Window. Widget will expand
        # to take up all the space in the window by default.
        self.setCentralWidget(widget)

app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

Run it! You'll see a window appear containing all the widgets we've created.

Big ol' list of widgets on Windows, Mac & Ubuntu Linux. Big ol' list of widgets on Windows, Mac & Ubuntu Linux.

You can learn how layouts work in Qt in our PyQt6 Layouts tutorial.

Let's have a look at all the example widgets, from top to bottom:

Over 10,000 developers have bought Create GUI Applications with Python & Qt!
Create GUI Applications with Python & Qt6
Take a look

Downloadable ebook (PDF, ePub) & Complete Source code

Also available from Leanpub and Amazon Paperback

[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]

Widget What it does
QCheckbox A checkbox
QComboBox A dropdown list box
QDateEdit For editing dates and datetimes
QDateTimeEdit For editing dates and datetimes
QDial Rotatable dial
QDoubleSpinBox A number spinner for floats
QFontComboBox A list of fonts
QLCDNumber A quite ugly LCD display
QLabel Just a label, not interactive
QLineEdit Enter a line of text
QProgressBar A progress bar
QPushButton A button
QRadioButton A toggle set, with only one active item
QSlider A slider
QSpinBox An integer spinner
QTimeEdit For editing times

There are far more widgets than this, but they don’t fit so well! You can see them all by checking the Qt documentation.

Next, we'll step through some of the most commonly used widgets and look at them in more detail. To experiment with the widgets we'll need a simple application to put them in. Save the following code to a file named app.py and run it to make sure it's working:

python
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QDial,
    QDoubleSpinBox,
    QLabel,
    QLineEdit,
    QListWidget,
    QMainWindow,
    QSlider,
    QSpinBox,
)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("My App")

app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

In the code above we've imported a number of Qt widgets. Now we'll step through each of those widgets in turn, adding them to our application and seeing how they behave.

QLabel

We'll start the tour with QLabel, arguably one of the simplest widgets available in the Qt toolbox. This is a simple one-line piece of text that you can position in your application. You can set the text by passing in a str as you create it:

python
widget = QLabel("Hello")

Or, by using the .setText() method:

python
widget = QLabel("1")  # The label is created with the text 1.
widget.setText("2")   # The label now shows 2.

You can also adjust font parameters, such as the size of the font or the alignment of text in the widget:

python
class MainWindow(QMainWindow):

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

        self.setWindowTitle("My App")

        widget = QLabel("Hello")
        font = widget.font()
        font.setPointSize(30)
        widget.setFont(font)
        widget.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)

        self.setCentralWidget(widget)

QLabel on Windows, Mac & Ubuntu Linux. QLabel on Windows, Mac & Ubuntu Linux.

Font tip Note that if you want to change the properties of a widget font it is usually better to get the current font, update it and then apply it back. This ensures the font face remains in keeping with the desktop conventions.

The alignment is specified by using a flag from the Qt. namespace. The flags available for horizontal alignment are:

PyQt6 flag (long name) Behavior
Qt.AlignmentFlag.AlignLeft Aligns with the left edge.
Qt.AlignmentFlag.AlignRight Aligns with the right edge.
Qt.AlignmentFlag.AlignHCenter Centers horizontally in the available space.
Qt.AlignmentFlag.AlignJustify Justifies the text in the available space.

The flags available for vertical alignment are:

PyQt6 flag (long name) Behavior
Qt.AlignmentFlag.AlignTop Aligns with the top.
Qt.AlignmentFlag.AlignBottom Aligns with the bottom.
Qt.AlignmentFlag.AlignVCenter Centers vertically in the available space.

You can combine flags together using pipes (|), however note that you can only use vertical or horizontal alignment flag at a time:

python
align_top_left = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop

Note that you use an OR pipe (`|`) to combine the two flags (not A & B). This is because the flags are non-overlapping bitmasks. e.g. Qt.AlignmentFlag.AlignLeft has the hexadecimal value 0x0001, while Qt.AlignmentFlag.AlignBottom is 0x0040. By ORing together we get the value 0x0041 representing 'bottom left'. This principle applies to all other combinatorial Qt flags. If this is gibberish to you, feel free to ignore and move on. Just remember to use |

Finally, there is also a shorthand flag that centers in both directions simultaneously:

PyQt6 Flag Behavior
Qt.AlignmentFlag.AlignCenter Centers horizontally and vertically

Weirdly, you can also use QLabel to display an image using .setPixmap(). This method accepts a pixmap, which you can create by passing an image filename to the QPixmap class from PyQt6.QtGui.

Below is an image which you can download for this example.

"Otje" the cat. "Otje" the cat.

Place the file in the same folder as your code, and then display it in your window as follows:

python
widget.setPixmap(QPixmap('otje.jpg'))

"Otje" the cat. "Otje" the cat, displayed in a window.

What a lovely face. By default the image scales while maintaining its aspect ratio. If you want it to stretch and scale to fit the window completely you can set .setScaledContents(True) on the QLabel:

python
widget.setScaledContents(True)

QCheckBox

The next widget to look at is QCheckBox() which, as the name suggests, presents a checkable box to the user. However, as with all Qt widgets there are number of configurable options to change the widget behaviors:

python
class MainWindow(QMainWindow):

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

        self.setWindowTitle("My App")

        widget = QCheckBox()
        widget.setCheckState(Qt.CheckState.Checked)

        # For tristate: widget.setCheckState(Qt.CheckState.PartiallyChecked)
        # Or: widget.setTristate(True)
        widget.stateChanged.connect(self.show_state)

        self.setCentralWidget(widget)

    def show_state(self, s):
        print(s == Qt.CheckState.Checked.value)
        print(s)

QCheckBox on Windows, Mac & Ubuntu Linux. QCheckBox on Windows, Mac & Ubuntu Linux.

You can set a checkbox state programmatically using .setChecked or .setCheckState. The former accepts either True or False representing checked or unchecked, respectively. However, with .setCheckState you also specify a particular checked state using a Qt.CheckState flag:

PyQt6 flag (long name) Behavior
Qt.CheckState.Unchecked Item is unchecked
Qt.CheckState.PartiallyChecked Item is partially checked
Qt.CheckState.Checked Item is checked

A checkbox that supports a partially-checked (Qt.CheckState.PartiallyChecked) state is commonly referred to as 'tri-state', that is being neither on nor off. A checkbox in this state is commonly shown as a greyed-out checkbox, and is commonly used in hierarchical checkbox arrangements where sub-items are linked to parent checkboxes.

If you set the value to Qt.CheckState.PartiallyChecked the checkbox will become tristate. You can also set a checkbox to be tri-state without setting the current state to partially checked by using .setTristate(True).

You may notice that when the script is running, the current state number is displayed as an int with checked = 2, unchecked = 0, and partially checked = 1. You don't need to remember these values, the Qt.CheckState.Checked flag == 2, for example. This is the value of these state's respective flags. This means that you can test state using state == Qt.CheckState.Checked.

QComboBox

The QComboBox is a drop down list, closed by default with an arrow to open it. You can select a single item from the list, with the currently selected item being shown as a label on the widget. The combo box is suited to selection of a choice from a long list of options.

You have probably seen the combo box used for selection of font faces, or size, in word processing applications. Although Qt actually provides a specific font-selection combo box as QFontComboBox.

You can add items to a QComboBox by passing a list of strings to .addItems(). Items will be added in the order they are provided:

python
class MainWindow(QMainWindow):

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

        self.setWindowTitle("My App")

        widget = QComboBox()
        widget.addItems(["One", "Two", "Three"])

        # Sends the current index (position) of the selected item.
        widget.currentIndexChanged.connect( self.index_changed )

        # There is an alternate signal to send the text.
        widget.currentTextChanged.connect( self.text_changed )

        self.setCentralWidget(widget)


    def index_changed(self, i): # i is an int
        print(i)

    def text_changed(self, s): # s is a str
        print(s)

QComboBox on Windows, Mac & Ubuntu Linux. QComboBox on Windows, Mac & Ubuntu Linux.

The .currentIndexChanged signal is triggered when the currently selected item is updated, by default passing the index of the selected item in the list. There is also a .currentTextChanged signal which instead provides the label of the currently selected item, which is often more useful.

QComboBox can also be editable, allowing users to enter values not currently in the list and either have them inserted, or simply used as a value. To make the box editable:

python
widget.setEditable(True)

You can also set a flag to determine how the insert is handled. These flags are stored on the QComboBox class itself and are listed below:

PyQt6 flag (long name) Behavior
QComboBox.InsertPolicy.NoInsert No insert
QComboBox.InsertPolicy.InsertAtTop Insert as first item
QComboBox.InsertPolicy.InsertAtCurrent Replace currently selected item
QComboBox.InsertPolicy.InsertAtBottom Insert after last item
QComboBox.InsertPolicy.InsertAfterCurrent Insert after current item
QComboBox.InsertPolicy.InsertBeforeCurrent Insert before current item
QComboBox.InsertPolicy.InsertAlphabetically Insert in alphabetical order

To use these, apply the flag as follows:

python
widget.setInsertPolicy(QComboBox.InsertPolicy.InsertAlphabetically)

You can also limit the number of items allowed in the box by using .setMaxCount, e.g:

python
widget.setMaxCount(10)

For a more in-depth look at the QComboBox take a look at our QComboBox documentation.

QListWidget

This widget is similar to QComboBox, except options are presented as a scrollable list of items. It also supports selection of multiple items at once. A QListWidget offers an currentItemChanged signal which sends the QListWidgetItem (the element of the list widget), and a currentTextChanged signal which sends the text of the current item:

python
class MainWindow(QMainWindow):

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

        self.setWindowTitle("My App")

        widget = QListWidget()
        widget.addItems(["One", "Two", "Three"])

        widget.currentItemChanged.connect(self.index_changed)
        widget.currentTextChanged.connect(self.text_changed)

        self.setCentralWidget(widget)

    def index_changed(self, i): # Not an index, i is a QListWidgetItem
        print(i.text())

    def text_changed(self, s): # s is a str
        print(s)

QListWidget on Windows, Mac & Ubuntu Linux. QListWidget on Windows, Mac & Ubuntu Linux.

QLineEdit

The QLineEdit widget is a simple single-line text editing box, into which users can type input. These are used for form fields, or settings where there is no restricted list of valid inputs. For example, when entering an email address, or computer name:

python
class MainWindow(QMainWindow):

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

        self.setWindowTitle("My App")

        widget = QLineEdit()
        widget.setMaxLength(10)
        widget.setPlaceholderText("Enter your text")

        #widget.setReadOnly(True) # uncomment this to make readonly

        widget.returnPressed.connect(self.return_pressed)
        widget.selectionChanged.connect(self.selection_changed)
        widget.textChanged.connect(self.text_changed)
        widget.textEdited.connect(self.text_edited)

        self.setCentralWidget(widget)


    def return_pressed(self):
        print("Return pressed!")
        self.centralWidget().setText("BOOM!")

    def selection_changed(self):
        print("Selection changed")
        print(self.centralWidget().selectedText())

    def text_changed(self, s):
        print("Text changed...")
        print(s)

    def text_edited(self, s):
        print("Text edited...")
        print(s)

QLineEdit on Windows, Mac & Ubuntu Linux. QLineEdit on Windows, Mac & Ubuntu Linux.

As demonstrated in the above code, you can set a maximum length for the text in a line edit.

The QLineEdit has a number of signals available for different editing events including when return is pressed (by the user), when the user selection is changed. There are also two edit signals, one for when the text in the box has been edited and one for when it has been changed. The distinction here is between user edits and programmatic changes. The textEdited signal is only sent when the user edits text.

Additionally, it is possible to perform input validation using an input mask to define which characters are supported and where. This can be applied to the field as follows:

python
widget.setInputMask('000.000.000.000;_')

The above would allow a series of 3-digit numbers separated with periods, and could therefore be used to validate IPv4 addresses.

QSpinBox and QDoubleSpinBox

QSpinBox provides a small numerical input box with arrows to increase and decrease the value. QSpinBox supports integers while the related widget QDoubleSpinBox supports floats:

python
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My App")

        widget = QSpinBox()
        # Or: widget = QDoubleSpinBox()

        widget.setMinimum(-9)
        widget.setMaximum(3)
        # Or: widget.setRange(-9, 3)

        widget.setPrefix("$")
        widget.setSuffix("c")
        widget.setSingleStep(3)  # Or e.g. 3.0 for QDoubleSpinBox
        widget.valueChanged.connect(self.value_changed)
        widget.textChanged.connect(self.value_changed_str)

        self.setCentralWidget(widget)

    def value_changed(self, i):
        print(i)

    def value_changed_str(self, s):
        print(s)

Run it and you'll see a numeric entry box. The value shows pre and post fix units, and is limited to the range 3 to -9.

It makes sense to set the step value to a multiple of the interval limits. Otherwise, you'll get weird behaviors. For example, if you change the lower limit from -9 to -10 and keep the step as 3, then once you reach the -10 limit, you won't be able to reach the 0 value again.

QSpinBox on Windows, Mac & Ubuntu Linux. QSpinBox on Windows, Mac & Ubuntu Linux.

The demonstration code above shows the various features that are available for the widget.

To set the range of acceptable values, you can use setMinimum and setMaximum, or alternatively, use setRange to set both simultaneously. Annotation of value types is supported with both prefixes and suffixes that can be added to the number, e.g. for currency markers or units using .setPrefix and .setSuffix, respectively.

Clicking on the up and down arrows on the widget will increase or decrease the value in the widget by an amount, which can be set using .setSingleStep. Note that this has no effect on the values that are acceptable to the widget.

Both QSpinBox and QDoubleSpinBox have a .valueChanged signal that fires whenever their value is altered. The raw .valueChanged signal sends the numeric value (either an int or a float), while .textChanged sends the value as a string, including both the prefix and suffix characters.

You can optionally disable text input on the spin box line edit, by setting it to read-only. With this set, the value can only be changed using the controls. This also has the side effect of disabling the flashing cursor:

python
widget.lineEdit().setReadOnly(True)

QSlider

QSlider provides a slide-bar widget, which functions internally much like a QDoubleSpinBox. Rather than display the current value numerically, it is represented by the position of the slider handle along the length of the widget. This is often useful when providing adjustment between two extremes, but where absolute accuracy is not required. The most common use of this type of widget is for volume controls.

There is an additional .sliderMoved signal that is triggered whenever the slider moves position and a .sliderPressed signal that emits whenever the slider is clicked:

python
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My App")

        widget = QSlider()

        widget.setMinimum(-10)
        widget.setMaximum(3)
        # Or: widget.setRange(-10,3)

        widget.setSingleStep(3)

        widget.valueChanged.connect(self.value_changed)
        widget.sliderMoved.connect(self.slider_position)
        widget.sliderPressed.connect(self.slider_pressed)
        widget.sliderReleased.connect(self.slider_released)

        self.setCentralWidget(widget)

    def value_changed(self, i):
        print(i)

    def slider_position(self, p):
        print("Position", p)

    def slider_pressed(self):
        print("Pressed!")

    def slider_released(self):
        print("Released")

Run this and you'll see a slider widget. Drag the slider to change the value.

QSlider on Windows, Mac & Ubuntu Linux. QSlider on Windows, Mac & Ubuntu Linux.

You can also construct a slider with a vertical or horizontal orientation by passing the orientation in as you create it. The orientation flags are defined in the Qt. namespace. For example --

python
widget = QSlider(Qt.Orientation.Vertical)

Or --

python
widget = QSlider(Qt.Orientation.Horizontal)

QDial

Finally, the QDial is a rotatable widget that functions just like the slider, but appears as an analogue dial. This looks nice, but from a UI perspective is not particularly user friendly. However, they are often used in audio applications as representation of real-world analogue dials:

python
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My App")

        widget = QDial()
        widget.setRange(-10, 100)
        widget.setSingleStep(1)

        widget.valueChanged.connect(self.value_changed)
        widget.sliderMoved.connect(self.slider_position)
        widget.sliderPressed.connect(self.slider_pressed)
        widget.sliderReleased.connect(self.slider_released)

        self.setCentralWidget(widget)

    def value_changed(self, i):
        print(i)

    def slider_position(self, p):
        print("position", p)

    def slider_pressed(self):
        print("Pressed!")

    def slider_released(self):
        print("Released")

Run this and you'll see a circular dial, rotate it to select a number from the range.

QDial on Windows, Mac & Ubuntu Linux. QDial on Windows, Mac & Ubuntu Linux.

The signals are the same as for QSlider and retain the same names (e.g. .sliderMoved).

Conclusion

This concludes our brief tour of the common widgets used in PyQt applications. To see the full list of available widgets, including all their signals and attributes, take a look at the Qt documentation.

For a more in-depth look at some of the discussed widgets, see our documentation for the QComboBox, QPushButton, QCheckBox, and QLineEdit widgets.

Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

More info Get the book

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

PyQt6 Widgets was written by Martin Fitzpatrick with contributions from Leo Well .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.