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:
- QVBoxLayout arranges Widgets vertically.
- QHBoxLayout arranges Widgets horizontally.
- QGridLayout arranges Widgets in a grid.
- 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 QPushButton
s 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:
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:
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:
This window contains 5 buttons which are arranged as we specified in the addWidget()
method. The method itself accepts 3 arguments:
- The Widget which should be placed in the grid.
- The row into which it should be placed.
- 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:
This window contains 2 labels and 2 QLineEdit
fields added via the addRow()
method. addRow()
accepts 2 arguments:
- Label Text (String)
- 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:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
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
: TheQtCore
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 theQtCore
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 passedboolean
.
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:
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:
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:
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 constantssetMaxLength()
is setting a maximum number of characters the user cannot surpasstext()
- retrieves the text within aQLineEdit
setText()
- sets text into aQLineEdit
clear()
will erase all the contents of theQLineEdit
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 collectionaddItems()
will add each of the strings in the given list to the collectionClear()
is used to delete all items in the collectioncount()
is used to retrieve the number of items in the collectioncurrentText()
is used to retrieve the text of the currently selected itemitemText()
accepts anindex
argument and returns the text of that itemcurrentIndex()
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:
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 checkboxsetText()
sets the label associated with the button or checkboxtext()
will retrieve the button's/checkbox's labelisChecked()
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:
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 rowssetColumnCount()
sets the number of columnssetHorizontalHeaderLabels()
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:
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 Widgetclear()
to clear all data from the TreeeditItem()
to edit a certain item in the TreeaddTopLevelItem()
to add a top-level itemaddTopLevelItems()
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:
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.