Beginners guide to learning GUI programming using PyQT/PySide(Part-1)

Paul Art
10 min readOct 26, 2021

--

Table of contents.

part-1

  1. Introduction.
  2. Prerequisites.
  3. Understanding event loop.
  4. Widgets.
  5. Working with PyQt Widgets.
  6. Layout managers.
  7. PyQt Structure.
  8. Signal Slots.

Part-2

  1. Recap
  2. styling
  3. Sample App.
  4. Debugging application.
  5. Advanced tutorial.
  6. Saving GUI states
  7. Further reading.

Introduction:

PyQt is one of the most powerful GUI frameworks for python. PyQt is a set of Python bindings for the Qt framework written in c++. It makes use of SIP to generate the python bindings.

If you are completely new to GUI programming, learning the PyQt framework can be difficult. In this tutorial we will focus more on how to learn by providing examples, making you understand the basic concepts for creating GUI’s(Graphical user interface), providing you sample designs so you can start creating projects, to help you get better at the framework.

This tutorial in no way is a complete guide to PyQt, as the framework is very huge and all of it cannot be covered in a single blog post. By the end of this tutorial, you should be able to know what to search and how to ask questions on Q/A forums, when you are stuck.

This tutorial will be using PyQt5 you may also follow along if you are here to learn PySide as there aren’t many differences between PyQt and PySide. I will also be linking Qt’s C++ documentation where ever required, don’t worry if you don’t understand the documentation, I’ll help you get started with the documentation at the end of the blog.

Please make sure you have installed PyQt5 to follow along.

How to approach this tutorial?

This will be quite a long tutorial so, I would suggest you take breaks in-between and take your own time to complete reading this. I would also suggest you keep a python editor open besides this page and when you see code, type it in the editor as opposed to copy-pasting. You should also experiment with the code yourself, so you can understand what is going on.

You must understand that you cannot get good at this framework in a day or two. You might not be able to grasp all of it at once, so, I recommend that you ask questions on Question-Answer forums such as Stack Overflow, read the Qt’s official documentation, and look into other tutorials and keep creating new projects.

Pre-requisite:

To continue reading this tutorial you must have at least basic knowledge in using classes and concepts of OOPs as throughout the tutorial we will be making use of OOPs concepts quite often.

Understanding the event loop:

Now every GUI program has an event loop (also called the main loop), which runs continuously until the user closes the window manually, programmatically, or the loop breaks due to unexpected errors.

You can imagine an event loop as a simple while loop that runs continuously until there is an interruption.

Note: Any code after the while loop will not be executed until it the program comes out of the while loop. This is very important to know, to avoid making mistakes that are quite common with beginners.

The above code is only for demonstration purposes. You may take a look into Qt’s source code to get the actual implementation.

Important terminologies in GUI programming.

Event: A change in the state of an object is termed an event. An event could be a simple mouse click, a keypress, etc.

Event source: An event source is an object that generates the event. for example when a button is clicked the button generates a mouse pressed event. So here the button that was clicked is the source.

Event Listener(sometimes called event handlers): Event listeners are objects that listen to events and handle the events accordingly.

Now let us try to relate this to the real world. A professor asks his students to take down notes. Students listen to this instruction and perform corresponding actions. Now imagine if no one was listening, do you think students would take notes? So you will have to listen to the instruction first to perform the required action.

Event loop working

Whenever an event is triggered a corresponding event handler is called and then continues to listen for more events in a loop.

The above diagram illustrates what happens when a user presses a button.

First, the user presses a button, that event is listened to, which then modifies the appearance or executes some block of code.

GUI Widgets:

widgets: Widgets are GUI components that help in displaying information and enabling the user to interact with the application. for example Buttons(eg: buttons used to submit forms), Labels(used to display text), etc.

PyQt framework provides many widgets, some of the most commonly used widgets are as listed below.

  1. QLabel- (Label) used to display inline texts and small information.
  2. QPushButton- (Button) Used to allow the user to click and perform certain tasks.
  3. QLineEdit- (inline textbox) Used to allow users to enter single line text.
  4. QTextEdit- (textbox)allows users to type multi-line text.

5. QCheckBox- (check box) used click checkmark on a list of items (eg: you might want to order dinner, so you will use this to check multiple items from the menu).

6. QRadioButton- (radio button) used to select only one item from a list of items. (eg: select male, or female)

Note: All the PyQt widgets name has prefix Q to them

Almost all the widgets in PyQt are inherited from QWidget class, so many widgets can have common functionalities(methods).

Working with PyQt widgets:

All PyQt programs start with QApplication() which handles widget-specific initialization and finalizations. There must only be one instance of the QApplication() class at the time of program execution. The exec() method of the QApplication class will start the event loop (the infinite while loop)

You can imagine it something like below(note: this is only for demonstration purposes. The actual implementation will be different).

class QApplication():

def exec(self):
while True:

for event in self.event_queue:

if event == "quit":
break

The QApplication can be found in the QtWidgets module. The QtWidgets will contain almost all the widgets including QLabel , QPushButton , QCheckBox etc.

Let’s start by creating a simple label:

Start by importing QLabel and QApplication from QtWidgets

Let's analyze the above code.

Let us start with the imports You will always find all the PyQt widgets in the QtWidgets module.

You then start by creating an instance of QApplication which is required by every PyQt Application.

Read what sys.argv does here.

The first parameter of the QLabel is a string which you would like to be displayed. The show() method of QLabel displays the window and exec() the method starts the event loop of the application.

Note: you should never create new widgets after exec method, as it won’t be executed until the event loop is stopped. As explained above once you enter a loop only code inside the loop gets executed, until the loop is broken.

Now let us try adding a button to the above application:

The QWidget is just the base widget of almost all the Qt widgets, we will be using this widget as a container to place our button and label using the move method. Now you could also have just used show on QPushButton, but that would have displayed our button in a new window, which is something we don’t want.

You must specify the parent argument if you want the label and the button to be displayed on the container(QWidget)

The move the method takes in two arguments x and y which is where the widget will be placed with respect to the top-left corner of the application

Note: The top-left corner is 0, 0

Layout Managers:

Now if you had tried resizing the above application. you would have noticed that the label and buttons don’t resize according to the window.

What you get
What you might have expected

Now usually you would have to manually calculate to adjust the widgets according to your window size, but since PyQt provides layout managers specifically designed for this purpose, you will not have to do the math to adjust your widgets accordingly.

Layout managers: Components are used to lay the widgets in relative positions and determine the size and position of the components within a container.

In simple terms, You will add widgets you want to the layout and the layout manager will automatically adjust the position and size of the widget according to its container.

Some of the common layout managers provided by PyQt:

  1. QBoxLayout-

quoted from qt docs:

QBoxLayout takes the space it gets (from its parent layout or from the parentWidget()), divides it up into a row of boxes, and makes each managed widget fill one box.

The QBoxLayout layout simply arranges the widgets in rows or columns as specified by the orientation(Horizontal, vertical), specified through arguments, or by using QBoxLayout().setDirection() method.

While you can use the QBoxLayout(QtCore.Qt.Vertical) it’s recommended that you use the convenience class provided by them, QVBoxLayout for vertical arrangements of widgets and QHBoxLayout for horizontal arrangement.

2. QGridLayout-

The GridLayout simply takes the space available from its parent and divides it into rows and columns. Which then allows you to place the widgets in the specified cell.

3. QFormLayout-

The QFormLayout is just for convenience. This layout manager lays its children in the form of two columns. This can be used when you are trying to create login forms etc. where you would require a label on the left side and a text field on the right side.

All the layouts inherits from QLayout class so there will be common methods that can be used across different layout managers

Now let us try to use one of the layout managers in our above application. We will be using QVBoxlayout to arrange the widgets vertically.

Let's now analyze the above code.

First, create a QVboxLayout instance and then use addWidget to add the widget to the layout. You will then have to set that layout to a parent widget, which would be the container. you can do this either by using QWidget.setLayout(v_layout) or by specifying the optional parent parameter in the QVBoxLayout eg: QVBoxLayout(parent=widget) .

rest of the code remains the same.

Using the QGridBox is similar the only difference is you need to specify the row and column number when adding the widget.

Eg:

for the addWidget the method you will have to specify the widget you want to add, then the row number you want to add the widget to, and then the column number.

PyQt Structure:

Knowing how PyQt Package is structured will help you in the import statements

You can imagine the PyQt structure as shown below (This structure is shown only to help you with the imports).

only commonly used module are listed, PyQt contains many more modules such as QtBluetooth, QtMultimedia etc.

.
└── PyQt/
├── QtWidgets/
│ └── [QPushButton, QLabel, QBoxLayout, QGridLayout...]
├── QtCore/
│ └── [QPoint, Qt, QPointF, QRect...]
├── QtGui/
│ └── [QPen, QBrush, QPalette, QColor..]
├── .
├── .
└── .

(The names in the square brackets are some of the commonly used classes.)

All the Widgets such as QPushButton, QLabel, QVBoxLayout etc. are in QtWidgets module.

All the classes that store numbers such as QPoint(x, y)(to store point), QRect(x, y, x1, y1) and Qt is in QtCore. The Qt class contains many important constants that we will be often using. (you don’t need to know this as of now)

The QtGui contains the common classes such as QPen , QBrushused for painting and drawing on widgets etc.

So if you now want to use QLabel you will access it as such

from PyQt5.QtWidgets import QLabel

To access QPoint use the import as shown below

from PyQt5.QtCore import QPoint

To access QColor make the import as

from PyQt5.QtGui import QColor

The above-shown imports can cause trouble if two modules have the same class name or function names.

for example both QtCore and QtGui contains Qt class, such cases can have namespace conflicts. To avoid that a better way to import would be to import it at the module level and the in use it as module.ClassName

for example:

from PyQt5 import QtWidgets, QtGui  # import modulesapp = QtWidgets.QApplication([])lbl_widget = QtWidgets.QLabel("sample text")color = QtGui.QColor("red")app.exec()

Signals and slots:

Now if you had pressed the button in the above application, you would have noticed that nothing happened, this is because you as a developer need to specify what would happen when the button is pressed. For this, you would be required to add an event listener.

In any GUI programming, you would want any change in the state of the widget to be notified to another widget or to the program so you can perform the corresponding action. for eg: When a button is pressed you might want the program to be notified so it can perform an action.

Usually, in other GUI toolkits, it's achieved through a callback mechanism in PyQt We will be using a signal slot mechanism. You don’t need to know the details. You may only if you are interested, take a look at Qt’s official documentation to know the difference.

Now let's implement a signal-slot mechanism to the above application.

QPushButton provides us with a clicked signal, which then has to be connected to a slot which would be a callable object.

Note: by default, all the methods and function returns None once the code under it is executed. You shouldn’t pass the called object, instead, you should let the PyQt call the function/method for you.

For example, your method is defined as below

def simple_function():
print("hello")

simple_function() <- The () makes this a function call, which means that the code under the function will be executed. You shouldn’t pass this as your slot function. This is quite a common mistake many beginners make.

simple_function is a callable object, which will not be executed until its called, which can be called using () at the end of simple_function.

Read part-2

Below is a music player that I created using PyQT you may check out the source code here: https://github.com/PaulleDemon/MusicApp

--

--

Paul Art

I open-source enthusiast, I have written and published many opensource libraries many of which are being used by over 30,000 developers.