Working with Python's PyQT Framework - Stack Abuse

Working with Python's PyQT Framework

Introduction

In this guide, we'll take a look at how to use Python's PyQT framework to develop a Graphical User Interface (GUI) for a desktop application in Python.

Popular Python alternatives for developing a GUI include Tkinter, Kivy, PySimpleGUI, and wxPython.

Note: As of writing this tutorial, PyQt6 is the latest and most advanced version of Python's PyQT framework, and also the version we will be using.

We'll be going through the installation process, and get familiar with the key elements of PyQT, before moving forward to Layout Managers, Widgets, Signals and Slots as well as how to Style Widgets, as well as take a look at UI Files, and how to create UIs via an intuitive drag-and-drop interface, which we can then export into runnable Python scripts:

  • Installation

  • Introduction to PyQt

  • Layout Managers

  • Widgets

    • Labels
    • Buttons
    • Line Edits
    • Combo Boxes
    • Radio Buttons
    • Displaying Data Using a Table Widget
    • Displaying Data using a Tree Widget
  • Signals and Slots

  • Styling the Widgets Applications

  • UI Files

    • qtDesigner
    • Converting UI Files to Python
  • Conclusion

Installation

To make use of the PyQt framework we first need to install it using pip package manager.

If you have pip installed onto your system, let's run the following command to install the newest version of PyQt:

$ pip install pyqt6

Should pip install pyqt6 fail, you can check for installation changes here.

Introduction to PyQt

PyQt is a toolkit that is the product of the Qt library and the Python programming language. As PyQt is one of the most commonly used GUI Frameworks for Python, there is both tons of well-written documentation and a large community.

One of the core classes of PyQt is the QWidget class - the implementation of a Widget. Widgets are GUI Components and the core building blocks of user interfaces. A widget can be a label, button, menu, combo box, scroller, toolbar, file dialog, etc...

There are many Widgets, and getting used to them takes time. We'll be going through the most commonly used widgets that will for the most part be present in almost every PyQt application.

The relative order of these Widgets on an application's frame is dictated and managed by a Layout Manager. We'll also be taking a look at the available layout managers and how they influence the positioning of GUI components.

The entry point of every PyQt application is the QApplication class, which represents the application itself. It handles all of the initialization and the "canvas" we draw on.

Note: There is always only one QApplication instance, no matter the number of windows or modal boxes in your application.

Let's take a leap and initialize a PyQt application, and initialize a window with an empty canvas:

import sys
from PyQt6.QtWidgets import QApplication, QWidget
app = QApplication(sys.argv)
root = QWidget()
root.setWindowTitle('A Simple PyQt6 App')
root.setGeometry(100, 100, 280, 80)
root.show()
sys.exit(app.exec())

Running this code initializes a simple application:

Let's go over this initialization line-by-line.

Firstly, we import the built-in Python sys module that provides us with functions to manipulate the Python Runtime Environment. In our case, we'll use this module to handle the exit status of the application - when a user hits the "X" button:

import sys

Then, we can import QApplication (the basis) and QWidget (the GUI components) from the PyQt6.QtWidgets module:

from PyQt6.QtWidgets import QApplication, QWidget

Next, this line is a requirement of QT. It will initialize PyQT. The sys.argv contains a list of all the command line arguments passed to the application. Every GUI application you create must have exactly one instance of QApplication.

Now, since QApplication is responsible for the initialization of most of the elements involved in developing PyQt applications, we'll want to instantiate it first. The constructor accepts a sys.argv argument since you can also pass in command-line arguments:

app = QApplication(sys.argv)

Now, the QApplication itself doesn't actually have any windows. Running the application without a window will produce an invisible result to us. To actually introduce a window to the mix, we'll create a Root Widget, which is also known as a Window Widget. In any case, it represents the lower-most Widget that we'll be adding other components to:

root = QWidget()

Let's set a custom window title using setWindowTitle():

root.setWindowTitle('A Simple PyQt6 App')

The setGeometry() method accepts 4 arguments: x_coordinate, y_coordinate, width & height. The x_coordinate and y_coordinate define the origin point of the Window when displayed:

root.setGeometry(100, 100, 280, 80)

Now, to display the created GUI on the screen we call the show() method on the root:

root.show()

Finally, we execute the application via app.exec(), and run the application's main loop until the user closes it:

sys.exit(app.exec())

Layout Managers

PyQt’s Layout Managers provide us with a productive way of arranging PyQt Widgets on a GUI. The better we lay out our Widgets, the more polished and professional our GUI application can look. Having awkward, huge spaces between buttons without much space-utilization isn't very user friendly. The same goes for the other way around - if we put buttons too close, they'll become uncomfortably easy to misclick.

The most popular PyQt Layout Manager Classes are:

  1. QVBoxLayout arranges Widgets vertically.
  2. QHBoxLayout arranges Widgets horizontally.
  3. QGridLayout arranges Widgets in a grid.
  4. QFormLayout arranges Widgets in two columns.

When creating GUI applications with PyQt, you’ll often use more than one of the four general-purpose Layout Managers, even in the same application, for different kinds of windows.

Let's leverage previous example and upgrade it by adding several Widgets such as QPushButtons and managing them through a Layout Manager.

1. QVBoxLayout

A Box Layout Manager (both QVBox or QHBox) uses all the space it gets from its parent layout or Widget and divides it up into several boxes.

Each Widget managed by the manager will fill one box.

QVBoxLayout allows us to arrange our Widgets vertically. The layout adds the Widgets to itself from top to bottom, sequentially. So the first Widget added in your code will be the top-most Widget and the last Widget added in your code will be the bottom-most one in the layout.

Let's add several buttons to our application, through a QVBoxLayout:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget

# Create the Qt Application
app = QApplication(sys.argv)

# Create the root Widget/Window
window = QWidget()

# Create the Vertical Box Layout Manager, setting `window` as parent by passing it in the constructor.
layout = QVBoxLayout(window)

# Create and add the QPushButton Widgets to the `layout`
layout.addWidget(QPushButton('One'))
layout.addWidget(QPushButton('Two'))
layout.addWidget(QPushButton('Three'))
layout.addWidget(QPushButton('Four'))
layout.addWidget(QPushButton('Five'))

# Show the parent Widget
window.show()

# Run the main Qt loop and allow safe exiting
sys.exit(app.exec())

Once we run this code we can see the following window on our screen:

QVBoxLayout Example

This window contains 5 buttons which are arranged vertically, in a top-to-bottom manner. Nothing happens when we click them because we haven't added any logic for them quite yet.

2. QHBoxLayout

QHBoxLayout is a Box Layout that allows us to arrange our Widgets horizontally. The layout adds the Widgets to itself from left to right. So the first Widget added in your code will be the left-most Widget and the last Widget added in your code will be the right-most Widget in the layout.

Let's change out the vertical box with a horizontal one:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton, QHBoxLayout, QWidget

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget of the Widgets added to the layout
window = QWidget()

# Create the Horizontal Box Layout Manager, setting `window` as parent by passing it in the constructor
layout = QHBoxLayout(window)

# Create and add the QPushButton Widgets to the `layout`
layout.addWidget(QPushButton('One'))
layout.addWidget(QPushButton('Two'))
layout.addWidget(QPushButton('Three'))
layout.addWidget(QPushButton('Four'))
layout.addWidget(QPushButton('Five'))

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Once we run this code we can see the following window on our screen:

QHBoxLayout Example

This window contains 5 buttons which are arranged horizontally, in a left-to-right manner.

3. QGridLayout

A QGridLayout is used when we want to arrange Widgets in a grid of rows and columns. In this grid, using coordinates we can define each Widget's relative position as: (row, column).

Note: Both row and column must be integers.

QGridLayout also uses all the space it gets from its parent's layout or Widget and divides it into several boxes. As with the previous Layout Managers, each Widget goes into its own box. The number of boxes is automatically calculated depending on the number of Widgets and their coordinates.

Let's use a QGridLayout instead of the horizontal box layout:

#!/usr/bin/python

# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton, QGridLayout, QWidget

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget
window = QWidget()

# Create the buttons
button1 = QPushButton('One')
button2 = QPushButton('Two')
button3 = QPushButton('Three')
button4 = QPushButton('Four')
button5 = QPushButton('Five')

# Create the QGrid Layout Manager
layout = QGridLayout(window)

# Add button Widgets to the QGridLayout
# addWidget([object], [row number], [column number])
layout.addWidget(button1,0,0)
layout.addWidget(button2,1,0)
layout.addWidget(button3,2,0)
layout.addWidget(button4,0,1)
layout.addWidget(button5,0,2)

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Once we run this code we can see the following window on our screen:

QGridLayout Example

This window contains 5 buttons which are arranged as we specified in the addWidget() method. The method itself accepts 3 arguments:

  1. The Widget which should be placed in the grid.
  2. The row into which it should be placed.
  3. The column into which it should be placed.

There is an optional fourth argument, alignment, which defines the alignment option of each Widget inside its box. The default value (is Qt.Alignment.AlignCenter) signifies that each Widget should fill their entire box from the center outward. More on the Qt module in later sections.

Finally, there's also a columnSpan and rowSpan argument, which define whether a Widget spans multiple rows or columns:

addWidget(Widget, fromRow, fromColumn, rowSpan, columnSpan, Qt.Alignment)

Let's set the row and column spans as well as a Qt.Alignment (prior to PyQt6, this would be Qt.AlignLeft):

# New import other than the ones already present
from PyQt6.QtCore import Qt

# addWidget([object], [row number], [column number], [columnSpan], [rowSpan], Qt.Alignment)
layout.addWidget(button1, 0, 0, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button2, 1, 0, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button3, 2, 0, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button4, 0, 1, 1, 1, Qt.Alignment.AlignLeft)
layout.addWidget(button5, 0, 2, 1, 1, Qt.Alignment.AlignLeft)

You can AlignLeft, AlignTop, AlignBottom, AlignRight and AlignCenter. Resizing the window, we'll see that each button is aligned to the left of their own box, rather than the center:

Whereas, if we used AlignCenter or left it as default:

4. QFormLayout

The QFormLayout makes it easy to produce form layouts for desktop applications. It consists of two columns - one for the labels, and one for the inputs.

Tpically, the input Widget is a QLineEdit, QSpinBox, QComboBox, or similar input Widgets. Let's create a QFormLayout:

#!/usr/bin/python

# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit

def addLabel(layout, text):
    layout.addWidget(QLabel(text))

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget and the QVBoxLayout Layout Manager
window = QWidget()
layout = QVBoxLayout(window)

# Create a label Widget and add it to the layout
label = QLabel('Enter some text!')
layout.addWidget(label)

line_edit = QLineEdit()
layout.addWidget(line_edit)

# Create a QPushButton object with a caption on it
qbtn= QPushButton('Add Label')

# Add the QPushButton to the layout
layout.addWidget(qbtn)

# Close the application when the button is pressed
# Here I am using slots & signals, which I will demonstrate later in this tutorial
qbtn.clicked.connect(lambda:addLabel(layout, line_edit.text()))

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Once we run this code we can see the following window on our screen:

QFormLayout Example

This window contains 2 labels and 2 QLineEdit fields added via the addRow() method. addRow() accepts 2 arguments:

  1. Label Text (String)
  2. Input Widget (QWidget)

The method will automatically create and add a new QLabel object with our labelText as its text. Additionally, you can also add a QLabel argument instead of a string, which skips the automatic conversion:

layout.addRow(QLabel('Nickname:'), QLineEdit())
layout.addRow(QLabel('Score:'), QLineEdit())

This also results in:

Better understand your data with visualizations.

  •  30-day no-questions money-back guarantee
  •  Beginner to Advanced
  •  Updated regularly (latest update June 2021)
  •  Updated with bonus resources and guides

Widgets

Now that we're familiar with the Layout Managers that PyQt offers - let's jump into what they manage. Widgets are a key concept of Qt, and consequentially PyQt.

A Widget reflects a graphical component of the UI. A user interface is made up of multiple Widgets, arranged within the window. Each Widget contains a series of attributes and methods that allow us to model their appearance and behavior.

PyQt6 currently offers over 40 Widgets, and you can even create your custom Widgets.

Since PyQt5, there has been a reshuffling of the base classes into different modules. There are a couple of fundamental high-level modules used by PyQt6, which include:

  • Qt: All of the modules mentioned below can be found packed-together into this single module.
  • QtCore: The QtCore module contains all of the core non-graphical modules, used by other modules. Signals, Slots, etc... are implemented in this module.
  • QtWidgets: This module contains most of the Widgets available in PyQt6.
  • QtGui: QtGui extends the QtCore module and contains GUI components.
  • QtSql: This module implements database integration for SQL Databases.
  • QtMultimedia: Low-level multimedia functionality can be found in this module.
  • QtNetwork: Classes used to implement network programming (Sockets, SSL Handling, Network sessions, DNS, ...) can be found in this module.

In this section, we will focus on the QtWidgets module and the Widgets it has on-offer.

1. Labels

The most popular Widget, the label, is most commonly used to explain the purpose or the usage of your GUI such as annotating what a field is for.
We can create a label by calling the QLabel class. Keep in mind that this Widget does not provide any user interaction.

We can change the visual appearance of a label in various ways:

  • setAlignment() will align the caption as per alignment constants, which can be the following:
    • Alignment.AlignLeft
    • Alignment.AlignRight
    • Alignment.AlignCenter
    • Alignment.AlignJustify
  • Text() is used to retrieve the caption of a label.
  • setText() will, instead of retrieving the caption, set the caption of a label.
  • setIndent() will set the indentation.
  • setWordWrap() will wrap the words in a label, or not, depending on the passed boolean.

Now, let's make a small PyQt6 application using only Labels, to display some information about Belgium:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel
from PyQt6.QtCore import Qt

# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget and the QVBoxLayout Layout Manager
window = QWidget()
layout = QVBoxLayout(window)

# Create a label beforehand
firstLabel = QLabel('Countrycode: BE')
secondLabel = QLabel('Brussels waffles are the best food ever.')

# Add labels to layout, creating an anonymous label while adding
layout.addWidget(firstLabel)
layout.addWidget(secondLabel, alignment = Qt.Alignment.AlignJustify)
layout.addWidget(QLabel('The Belgian flag consists of the colors black, yellow and red', wordWrap=True), alignment = Qt.Alignment.AlignLeft)

# using setText() we can change the caption of a label
firstLabel.setText('Belgium is a country located in Europe')
firstLabel.setAlignment(Qt.Alignment.AlignRight)

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

You can create a QLabel beforehand like with firstLabel. Then, even after adding it to a layout - you can manipulate it and set the text, alignment, etc. via its setter methods. The latest states, as set by the setters will be drawn on the window in the end.

If you'd like to avoid creating objects beforehand and calling many methods - you can simply create a widget and add it right after, in the addWidget() call itself. We've set the wordWrap argument of the third QLabel to true, since it's a bit longer than the other two and we might want to wrap the words in case they're longer than the window can accommodate for.

Note: Since PyQt6, the Qt is a part of PyQt6.QtCore, and the Align_ options are a part of the Alignment class - resulting in Qt.Alignment.Align_ calls. Prior to PyQt6, Qt was a part of the PyQtX module, not the QtCore module, and the Align_ options were a part of Qt so the calls would look more like - Qt.Align_ instead.

If we run this code, we'll see our three labels, aligned as per our Alignment settings:

Labels Example

2. Signals and Slots

Signals and Slots in PyQt are used to communicate between objects. This mechanism is a central feature of the Qt framework.

For example, if a user were to click a Delete button, we want the window's delete() function to be called. For this, the 2 Widgets have to communicate with each other.

An event will be an action performed by a user in our GUI.

When an event occurs, a signal is emitted by the corresponding Widget. The Widgets available in Qt have many predefined signals, but you can always make extra custom signals.

A slot is a function that is called in response to a signal. Once again the Widgets available in Qt have many pre-defined slots, but it is very common practice to create your own.

The most useful features of Signals and Slots include:

  • A signal can be connected to another signal
  • A signal can be connected to one or many slots
  • A slot may be connected to one or many signals

The general syntax to connect a signal to a slot is:

widget.signal.connect(slot_function) 

This code will connect the slot_function to Widget.signal, and whenever the signal is emitted, the slot_function() function will be called.

To avoid unexpected behavior it is important to annotate every slot function with the @pyqtSlot() decorator:

from PyQt6.QtCore import pyqtSlot

# Slot function - Note the @pyqtSlot() annotation!
@pyqtSlot()
def hello_world():
  print('Button is clicked, Hello World!')

Now, let's create an application that makes use of the Signals and Slots mechanism, by putting a button that simply prints a message to the console:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QPushButton
from PyQt6.QtCore import pyqtSlot

@pyqtSlot()
def hello_world():
  print('You shall not pass!')

# Create the Qt Application
app = QApplication(sys.argv)

# Create a QPushButton Object
button = QPushButton('Click me')

# Connect the button to the hello_world slot function
button.clicked.connect(hello_world)

# Show the button to the user
button.show()

# Run the main Qt loop
sys.exit(app.exec())

Once we run this code we can see the following window on our screen:

Signals and Slots Short Example

After running this code and clicking the button, it outputs the following text to the console:

You shall not pass!

3. Buttons

Now that we can label other GUI components on an application - let's take a look at the first interactive component we'll be implementing - a QButton. Buttons lead to outcomes - in our case, they can be used to invoke certain functions. There are a few pre-defined default buttons which are OK, Yes, No, Cancel, Apply and Close, though, you can also add custom text on them.

You can attach an event handler to a button that fires a function or any other piece of code when a button is pressed. Let's create a button that allows the user to add a QLabel to the screen.

Once a user enters some text into a QLineEdit, and a button press has been detected - we'll collect the data from a QLineEdit, and use that text to set the text of a new QLabel, which is then added to the layout.

Since buttons expect a callable function to be passed as the click event handler - we'll define a new function add_label() which can be used to add any QLabel to the specified layout:

def addLabel(layout, text):
    layout.addWidget(QLabel(text))

Now, let's write our GUI and call this function with the text provided by the user:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit

def addLabel(layout, text):
    layout.addWidget(QLabel(text))
    
# Create the Qt Application
app = QApplication(sys.argv)

# Create the parent Widget and the QVBoxLayout Layout Manager
window = QWidget()
layout = QVBoxLayout(window)

# Create a Qlabel Widget and add it to the layout
label = QLabel('Enter some text!')
layout.addWidget(label)

# Create a QLineEdit to collect user data
line_edit = QLineEdit()
layout.addWidget(line_edit)

# Create a QPushButton object with a caption on it
qbtn= QPushButton('Add Label')
layout.addWidget(qbtn)

# When clicked, perform a callable function - `addLabel()`
qbtn.clicked.connect(lambda:addLabel(layout, line_edit.text()))

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

Once we run this code we can write some text in the QLineEdit field, which is added to the layout as a QLabel once we press on Add Label:

Buttons Example

4. Line Edits

We've briefly taken a look at the QLineEdit widget two times now - let's take a moment to see what it offers. As seen before, it allows users to enter a line of text - these are the rudimentary way to collect user data be it for addition or editing of already existing data. We can of course perform common operations such as copy, paste, undo, redo while writing text within them.

Some of the common methods you'll be using with them are:

  • setAlignment() will once again, align the caption as per alignment constants
  • setMaxLength() is setting a maximum number of characters the user cannot surpass
  • text() - retrieves the text within a QLineEdit
  • setText() - sets text into a QLineEdit
  • clear() will erase all the contents of the QLineEdit

Let's rewrite the previous example, but this time already have some pre-defined text in a QLineEdit, change an already existing QLabel instead of adding a new one - and briefly explore the usage of PyQt Slots, which will be covered in more detail later on in the guide.

We'll create a rudimentary quote app, that has a corpus of famous quotes and gives you a random one on demand. You can expand this list by adding a new one and clicking Add Quote, which is then included in the quote pool when you decide to get a new random one via Get Random Quote:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit
from PyQt6.QtCore import pyqtSlot
import random

# Deifne helper functions as PyQt Slots
@pyqtSlot()
def randomQuote():
  # Set label to random quote from the list
  quoteLabel.setText(random.choice(quotes))

@pyqtSlot()
def addQuote():
    # Add new quote to the list and clear the input field
    quotes.append(newQuoteLineEdit.text())
    newQuoteLineEdit.clear()

app = QApplication(sys.argv)
window = QWidget()
layout = QVBoxLayout(window)

# Default quote list
quotes = ['Do or do not, there is no try.', 'The meaning of life is 42']

# Get a random quote for the user
quoteLabel = QLabel(random.choice(quotes))

# QLineEdit field to collect new quote information, and a button for it
newQuoteLineEdit = QLineEdit('Add new quote...')
addQuoteButton = QPushButton('Add New Quote')

# Button to get random quote
getQuoteButton = QPushButton('Get Random Quote')

# Add the previous Widgets to the layout
layout.addWidget(newQuoteLineEdit)
layout.addWidget(quoteLabel)
layout.addWidget(addQuoteButton)
layout.addWidget(getQuoteButton)

# On click - call the slots (functions)
getQuoteButton.clicked.connect(randomQuote)
addQuoteButton.clicked.connect(addQuote)

# Show the parent Widget
window.show()

# Run the main Qt loop
sys.exit(app.exec())

This results in:

5. Combo Boxes

Comboboxes allow users to pick from a list of options - similar to the <select> tag in HTML. These can be achieved through the QComboBox Widget. The basic QComboBox is a read-only Widget, meaning the user has to pick exclusively from the pre-defined list and can't add their own options. However, they can also be editable - which allows the user to add a new option if none fit their needs.

Given below are the most commonly used methods of the QComboBox class:

  • addItem() adds a string to the collection
  • addItems() will add each of the strings in the given list to the collection
  • Clear() is used to delete all items in the collection
  • count() is used to retrieve the number of items in the collection
  • currentText() is used to retrieve the text of the currently selected item
  • itemText() accepts an index argument and returns the text of that item
  • currentIndex() returns the index of the currently selected item

Let's create a mini ordering app, where a user selects an item from a menu and enters a comment for the restaurant. Then, when a button is clicked - this order is shown to the user:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox
from PyQt6.QtCore import pyqtSlot

@pyqtSlot()
def placeOrder():
    order_format = "Placed order for {} with comment '{}'"
    layout.addWidget(QLabel(order_format.format(comboBox.currentText(), commentLineEdit.text())))

app = QApplication(sys.argv)

window = QWidget()
layout = QVBoxLayout(window)

label1 = QLabel('Pick one of the following options:')
comboBox = QComboBox()
comboBox.addItems(['Pasta', 'Pizza', 'Lasagna'])

layout.addWidget(label1)
layout.addWidget(comboBox)

commentLineEdit = QLineEdit('Comment for the restaurant...')
placeOrderButton = QPushButton('Place order')

layout.addWidget(commentLineEdit)
layout.addWidget(placeOrderButton)

placeOrderButton.clicked.connect(placeOrder)

window.show()
sys.exit(app.exec())

Now, let's place an order and attach a request to it:

Combo Boxes Example

6. Radio Buttons and Check Boxes

Radio Buttons and Check Boxes are mostly used for the same purpose - allowing someone to select an option out of several. The only difference is - Radio Boxes are used when we'd like to limit the user to select one option, while Check Boxes are used when we'd like to allow the user to select multiple options.

For example, we might force the user to select between being a new or old customer (can't be both at the same time), but allow them to select multiple services they'd like to sign up for.

These are implemented as QRadioButton and QCheckBox, naturally. We can check if they're checked, set them to checked or unchecked, set their text as well as retrieve the text of their labels:

  • setChecked() checks the radio button or checkbox
  • setText() sets the label associated with the button or checkbox
  • text() will retrieve the button's/checkbox's label
  • isChecked() checks whether the button/checkbox is selected or not

Let's create a simple app that lets users select between a couple of the services that an imaginary car wash offers:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QRadioButton, QCheckBox

app = QApplication(sys.argv)
window = QWidget()
layout = QVBoxLayout(window)

label_customer = QLabel('Pick one of the following options:')

# Create two radio buttons for the customer, assuming they might be a new customer
qradioButton = QRadioButton('Old Customer')
qradioButton2 = QRadioButton('New Customer')
qradioButton2.setChecked(True)

layout.addWidget(label_customer)
layout.addWidget(qradioButton)
layout.addWidget(qradioButton2)

label_service = QLabel("Pick the services you'd like:")
qCheckBox = QCheckBox('Car Wash')
qCheckBox2 = QCheckBox('Car Polish')
qCheckBox3 = QCheckBox('Vacuuming')

layout.addWidget(label_service)
layout.addWidget(qCheckBox)
layout.addWidget(qCheckBox2)
layout.addWidget(qCheckBox3)

window.show()
sys.exit(app.exec())

This prompts us with a survey-like app that lets us choose whether we're an old or new customer, and allows us to pick between the services that the car wash offers:

Radio Buttons Example

7. Displaying Data Using a Table Widget

QTableWidget is a Widget that, without dealing with much configuration, allows us to create awesome excel-like tables in PyQt, in which we can display data.

Every table is an item-based table, with rows and columns.

Keep in mind that using a QTableWidget is not the only way to display information in tables. Data models can be created and displayed using the QTableView Widget as well. Though, the QTableWidget inherently uses a QTableView under the hood to actually create a table, so we'll be using the higher-level approach of using the table from the get-go.

Given the column-row nature of tables - we can comfortably create dictionaries to hold data for them or even lists of lists. When creating a table, we'll want to set the column and row count before adding any data to it, and then just populate it through loops:

  • setRowCount() sets the number of rows
  • setColumnCount() sets the number of columns
  • setHorizontalHeaderLabels() sets the labels of the horizontal headers

Now, let's create a simple application that contains a table with several nordic countries and their capitals:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6.QtWidgets import (QApplication, QTableWidget, QTableWidgetItem)
from PyQt6.QtGui import QColor

# Declare our table values
nordic_countries = [('Norway', 'Oslo', 'Yes'),
          ('Iceland', 'Reykjavik', 'Yes'),
          ('Denmark', 'Copenhagen', 'Yes'),
          ('Belgium', 'Brussels','No')]
          
# Create the Qt Application
app = QApplication(sys.argv)
table = QTableWidget()

# Configure QTableWidget to have a number of rows equivalent to the amount of items from the nordic_countries struct
table.setRowCount(len(nordic_countries))

# Since every country in our 'nordic_countries' variable has the same amount of attributes
# we take the amount (3) of the first country and use this as the number of columns
table.setColumnCount(len(nordic_countries[0]))

# Set the Horizontal headers using setHorizontalHeaderLabels()
table.setHorizontalHeaderLabels(['Country', 'Capital', 'Scandinavian?'])

# Loop through every country in our 'nordic_countries' variable
for i, (country, capital, scandinavian_bool) in enumerate(nordic_countries):

    # Make a QTableWidgetItem --> acts as an item in a table
    item_country = QTableWidgetItem(country)
    item_capital = QTableWidgetItem(capital)
    item_scandinavian_bool = QTableWidgetItem(scandinavian_bool)

    # Set the items: item, index, QTableWidgetItem
    table.setItem(i, 0, item_country)
    table.setItem(i, 1, item_capital)
    table.setItem(i, 2, item_scandinavian_bool)

# Finally show the table
table.show()

# Launch the application
sys.exit(app.exec())

Once we run this code we can see the following window on our screen:

Table Wigdet Example

8. Displaying Data Using a Tree Widget

Tree Widgets are really useful for displaying tree-like structures such as file hierarchies or sublists pertaining to specific lists of elements. To accommodate for this type of Widget, PyQt offers QTreeWidget.

Similar to how the QTableWidget is built on top of QTableView - the QTreeWidget is built on top of QTreeView.

The tree consists of headers and items. Headers are the column names. Each item can have multiple items assigned to it. An item, can for instance, be a directory, while its own items are the files within that directory - or an item can be a task, and its items are the people assigned to that task.

Some of the common methods we'll be using to work with Tree Widgets are:

  • setHeaderLabels() sets the column name for the Tree Widget
  • clear() to clear all data from the Tree
  • editItem() to edit a certain item in the Tree
  • addTopLevelItem() to add a top-level item
  • addTopLevelItems() to add a list of top-level items

Each item can be assigned to a parent component. For a top level item, we can assign it to the QTreeWidget itself, while for lower-level items - we can assign them to already existing top-level items. You can go as far down the line as you'd like with this, though, having too many levels can be confusing for the user.

Let's go ahead and make a quick shopping list - with Food and Furniture as the top-level items (categories of items we'd like to buy) and their children will be the actual items themselves:

#!/usr/bin/python
# Import all needed modules
import sys
from PyQt6 import QtWidgets

app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(window)

# Create the QTreeWidget Widget
tree_widget = QtWidgets.QTreeWidget()

# Set the column name for the Tree Widget
tree_widget.setHeaderLabels(['Items', 'Total Cost'])

# Populate first tree with QTreeWidgetItem objects
foodList = QtWidgets.QTreeWidgetItem(tree_widget, ['Food', '€ 15'])
QtWidgets.QTreeWidgetItem(foodList, ['Apples', '€ 6'])
QtWidgets.QTreeWidgetItem(foodList, ['Pears', '€ 4'])
QtWidgets.QTreeWidgetItem(foodList, ['Oranges', '€ 5'])

# Populate second tree with QTreeWidgetItem objects
furnitureList = QtWidgets.QTreeWidgetItem(tree_widget, ['Furniture', '€ 225'])
QtWidgets.QTreeWidgetItem(furnitureList, ['Table', '€ 150'])
QtWidgets.QTreeWidgetItem(furnitureList, ['Chairs', '€ 75'])

layout.addWidget(tree_widget)

window.show()
sys.exit(app.exec())

Once we run this code we can see the following window on our screen:

Tree Wigdet Example

Conclusion

In this guide, we've jumped into PyQt - Python's wrapper for the popular Qt library.

We've taken a look at some of the key concepts of the library, and jumped into working with it through Layout Managers, got familiar with Widgets and created several really simple demonstration applications that showcase how you can use them.

Last Updated: May 5th, 2021

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

Want a remote job?

    Prepping for an interview?

    • Improve your skills by solving one coding problem every day
    • Get the solutions the next morning via email
    • Practice on actual problems asked by top companies, like:
     
     
     

    © 2013-2021 Stack Abuse. All rights reserved.