In a previous tutorial, we talked about the Tkinter module and we saw how to build GUI apps using it. In this tutorial, we will continue building graphical interfaces in Python, and this time we will use PyQt5.

PyQt5 is one of the most used modules in building GUI apps in Python, and that’s due to its simplicity as you will see.

Another great feature that encourages developers to use PyQt5 is the PyQt5 designer, which makes it so easy to develop complex GUI apps in a short time. You just drag your widgets to build your form.

In this PyQt5 tutorial, I will use Python 3.6 on Windows 10, and I assume that you know some Python basics.

Sound great! So let’s get started and install PyQt5 at first, then we will see how to develop GUI apps with examples.

Install PyQt5

PyQt5 has two versions, the commercial version and the free GPL version that we will use in this tutorial.

To install PyQt5, you have two ways:

Using pip

Using source

Using pip

To install PyQt5 using pip, run the following command:

$ pip3 install PyQt5

To ensure the successful installation, run the following Python code:

import PyQt5

If no errors appeared, that means you have successfully installed PyQt5, but if you got errors, you might be using an unsupported version of Python.

Using source (On Linux)

To install PyQt5 from source, you have to do the following:

Install SIP.

Download PyQt5 source.

Configure & install.

As you might know, PyQt5 is a Python binding for the famous library Qt that is written in C++.

The tool that makes this binding is called SIP. So to install PyQt5 from source, you need at first to install SIP.

To install SIP, run the following command:

$ pip3 install PyQt5-sip

Now you are ready to download and install PyQt5 source.

Download PyQt5 source from here.

Then unpack the compressed source and run the following commands inside the root of the uncompressed folder:

$ python3 configure.py $ make $ make install

To ensure that everything is fine, try to import PyQt5 as we did before, and everything should be OK.

Using source (On Windows)

Since SIP needs the GCC compiler, you need to install MinGW, which is a Windows port of Linux GCC compiler.

The only thing you need to change is the configuration step; you need to tell Python about the platform:

$ python configure.py --platform win32-g++ $ make $ make install

Congratulations! Now you have successfully installed PyQt5 from the source.

Install PyQt5 designer

There are two ways to build GUI apps using PyQt5:

Design widgets by code.

Using PyQt5 designer.

In this PyQt5 tutorial, we will use the PyQt5 designer, which makes it so easy to finish a lot of work in a matter of seconds.

PyQt5 designer comes with PyQt5 tools. To install it, you need to install PyQt5 tools.

$ pip3 install PyQt5-tools

Where is PyQt5 designer?

After successful installation, you can find the PyQt5 designer on this location:

C:\Program Files\Python36\Lib\site-packages\pyqt5-tools\

Also, If you installed Python for your current user only, you will find the PyQt5 designer on this location:

C:\Users\LikeGeeks\AppData\Local\Programs\Python\Python36-32\Lib\site-packages\ pyqt5-tools\

You can make a shortcut for it instead of going into this location every time you want to run the PyQt5 designer.

How to use PyQt5 designer

Open designer.exe, and you will see a dialog asking you about the form template you want.

There are five templates available:

Dialog with Buttons Bottom: Creates a form with OK and Cancel buttons at the bottom right of the form.

Dialog with Buttons Right: Creates a form with OK and Cancel buttons at the top right of the form.

Dialog without Buttons: Creates a blank form.

Main Window: Creates a window with a menu bar and a toolbar and inherited from QMainWindow.

Widget: Creates a widget that is inherited from QWidget class, unlike the Dialogs templates which inherit from QDialog class.

So we have three types of templates, what is the difference?

Difference between QDialog, QMainWindow, and QWidget

QWidget is the base class for all GUI elements in the PyQt5.

QDialog is used for asking the user about something, like asking the user to accept or reject something or maybe asking for input and is based on QWidget.

QMainWindow is the bigger template where you can place your toolbar, menu bar, status bar, and other widgets. It doesn’t have a built-in allowance for buttons like those in QDialog.

Load .ui VS convert .ui to .py

In this tutorial, we will use the PyQt5 designer, but before we dig deeper, let’s see how we will use the generated design from the PyQt5 designer.

Open PyQt5 designer, and choose Main Window template and click create button.

Then from the file menu, click save; PyQt5 designer will export your form into an XML file with .ui extension. Now, to use this design, you have two ways:

Loading the .ui file in your Python code.

Converting the .ui file to a .py file using pyuic5.

Loading the .ui file in your Python code

To load the .ui file in your Python code, you can use the loadUI() function from uic like this:

from PyQt5 import QtWidgets, uic import sys app = QtWidgets.QApplication([]) win = uic.loadUi("mydesign.ui") #specify the location of your .ui file win.show() sys.exit(app.exec())

If you run your code, you should see a window with nothing but a label.

That means the ui file loaded successfully!

We used sys.exit(app.exec()) instead of using app.exec() directly to send the correct status code to the parent process or the calling process.

If you used app.exec() directly, the application would send zero, which means success, and this will happen even if the application crashed.

Converting the .ui file to a .py file using pyuic5

Now, let’s try the second way by converting the .ui file to a Python code:

$ pyuic5 mydesign.ui -o mydesign.py

Yes! That creates a new file with the name mydesign.py. Now, let’s import that file to show our window.

The pyuic5 stands for Python user interface converter version 5.

from PyQt5 import QtWidgets from mydesign import Ui_MainWindow # importing our generated file import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

If you run this code, you should see the same window again as we did in the first method.

The benefit of using the second method is the auto-completion that the IDE will provide since all of your widgets are imported, while the first method you just load the .ui file, and you need to be aware of your widgets names.

Another benefit of using the second method is the speed since you don’t need XML parsing to load the UI.

[click_to_tweet tweet=”Converting the .ui file to a .py file is safer in coding and faster in loading!” quote=”So we can say that converting the .ui file to a .py file is safer in coding and faster in loading!” theme=”style6″]

Now, let’s get our hands dirty and play with the PyQt5 widgets.

QLabel widget

To add a QLabel widget to your form, do the following:

Open PyQt5 designer and choose the Main Window template.

Drag a label widget from the widget box on the left.

Now, save the design to a .ui file and convert it to a .py file and let’s play with the label widget using code.

Change Font

To change the QLabel font, use the setFont() method and pass a QFont to it like this:

from PyQt5 import QtWidgets, QtGui from mydesign import Ui_MainWindow import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.label.setFont(QtGui.QFont('SansSerif', 30)) # change font type and size app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

If you run this code, you will note that the label does not appear correctly because the size is smaller than the font size we used. So we need to set the label size.

Change size

To change the QLabel size, you need to set its geometry using the setGeometry() method like this:

from PyQt5 import QtWidgets, QtGui,QtCore from mydesign import Ui_MainWindow import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.label.setFont(QtGui.QFont('SansSerif', 30)) self.ui.label.setGeometry(QtCore.QRect(10, 10, 200, 200)) # change label geometry app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

Change text

To change the QLabel text, you can use the setText() method like this:

from PyQt5 import QtWidgets, QtGui,QtCore from mydesign import Ui_MainWindow import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.label.setFont(QtGui.QFont('SansSerif', 30)) self.ui.label.setGeometry(QtCore.QRect(10, 10, 200, 200)) self.ui.label.setText("LikeGeeks") #change label text app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

That was easy! Let’s check some other widgets.

QLineEdit widget

The QLineEdit is an editable place where you can accept input from the user. LineEdit has many methods to work with.

I will create a new design with the PyQt5 designer, and I’ll add six QLineEdit widgets, and I’ll export it to .py file.

Now, let’s see some QLineEdit methods:

from PyQt5 import QtWidgets,QtCore from mydesign import Ui_MainWindow import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.lineEdit.setText("Welcome to LikeGeeks website") #change text self.ui.lineEdit_2.setMaxLength(10) #set maximum length self.ui.lineEdit_3.setEchoMode(QtWidgets.QLineEdit.Password) # password input self.ui.lineEdit_4.setReadOnly(True) #QLineEdit readonly self.ui.lineEdit_5.setStyleSheet("color: rgb(28, 43, 255);") #change text color self.ui.lineEdit_6.setStyleSheet("background-color: rgb(28, 43, 255);") #change QLineEdit background color app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

The first QlineEdit, we changed the text using the setText() method.

The second QlineEdit, we set the maximum allowed characters to 10, so nothing more is accepted.

The third QlineEdit, we set it to password mode, so all your input appears as asterisks.

The fourth QlineEdit, we set it to read-only so you can’t edit its content.

The fifth QlineEdit, we changed the font color using the setStyleSheet() method, and we insert the color like web pages CSS values.

The sixth QlineEdit, we changed the background color using the setStyleSheet() method.

The setStyleSheet() method

You can use the setStyleSheet() method with all PyQt5 widgets to change the style.

You can change the following using setStyleSheet() method:

Font type and size

Text Color

Background color

Border color

Border top color

Border bottom color

Border right color

Border left color

Selection color

Selection background color

These are the most important values you can pass to the setStyleSheet() method.

QPushButton Widget

Most of your Python programs will have this QPushButton widget. You click the button, and some code runs.

If you have a programming background, you may hear about event handling where you interact with a widget, and a function is executed.

The idea in PyQt5 is the same, but the definitions are a bit different.

[click_to_tweet tweet= “The click event in PyQt5 is called a signal and the method which gets executed is called a slot.” quote= “The click event in PyQt5 is called a signal and the method which gets executed is called a slot.” theme=” style6”]

So when you click a QPushButton, a signal is emitted. The signal name in this case is called clicked().

To bind the emitted signal with a slot, you need to use the connect() method as you will see now.

This event handling process continues to work until you close your form or main widget.

Let’s build a form with a QLabel and a QPushButton and export it to a .py file.

Now, We will connect the clicked() signal with a slot using connect() method like this:

self.ui.pushButton.clicked.connect(self.btnClicked)

The btnClicked here is the slot or the function that will run when you click the QPushButton.

So your code will be like this:

from PyQt5 import QtWidgets from mydesign import Ui_MainWindow import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.pushButton.clicked.connect(self.btnClicked) # connecting the clicked signal with btnClicked slot def btnClicked(self): self.ui.label.setText("Button Clicked") app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

Awesome!

Visual signal/slot editor

We saw how to connect the widget signal to a slot using the connect() method, but this is not the only way.

There are some predefined slots for each widget. You can connect a signal to any predefined slot without coding in the PyQt5 designer.

Drag a QPushButton and a QLineEdit on your form.

Press F4 and drag the mouse from the QPushButton and release it on the top of the QLineEdit.

The signal/slot editor will show up.

On the left, the predefined signals while on the right the predefined slots. Let’s say that we want to connect the clicked() signal with the clear slot.

Choose the clicked from the left and choose clear from the right and click OK.

After finishing your signal/slot connections, you can escape from this mode by pressing ESC or F3.

Now, if you run this form and click the QPushButton, any text on the QLineEdit will be cleared. You can edit or delete this connection from the signal/slot editor panel.

How to emit a signal

We saw how signals and slots work. All signals we worked with are predefined for us.

What about emitting our custom signal?

Very easy! You can do that by using the pyqtSignal class as follows:

Define your event with type pyqtSignal.

Call emit() method at the place you want your event to be fired.

Let’s say we have a nut class, and we want to fire the cracked signal to be emitted when we crack it.

from PyQt5.QtCore import pyqtSignal,QObject class nut(QObject): cracked = pyqtSignal() def __init__(self): QObject.__init__(self) def crack(self): self.cracked.emit()

How to use a signal

Now, let’s make our example more practical by instantiating an instance of the nut class and emitting the cracked signal:

def crackit(): print("hazelnut cracked!") hazelnut = nut() hazelnut.cracked.connect(crackit) # connecting the cracked signal with crackit slot hazelnut.crack()

The cracked signal was successfully emitted.

Signal (event) overriding

Sometimes, you need to override the default behavior for a specific signal or event.

Let’s see a practical example for that. If you want to close the main window when the user presses a specific key, you can override the keyPressEvent inside your main window like this:

def keyPressEvent(self, e): if e.key() == Qt.Key_F12: self.close()

Now, if the user presses the F12 key, the main window will be closed.

Here we override the keypress signal of the main window and close the window.

QComboBox widget

Instead of letting the user enter values in a QLineEdit or any editable widget, we can use a QCombobBox widget to give the user a list of choices to select from.

Let’s drag a combo box to our form and take a look at some of its methods.

If you run the app now, you will note that the QComboBox is empty. To add items to the QComboBox, use the addItem() method:

from PyQt5 import QtWidgets from mydesign import Ui_MainWindow import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.comboBox.addItem("First item") #add item self.ui.comboBox.addItem("Second item") app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

Get all items

There is no direct method to get all items from a QComboBox, but you can use a for loop to do that.

for i in range(self.ui.comboBox.count()): print(self.ui.comboBox.itemText(i))

Select an item

To select an item from the QComboBox, you have two methods:

self.ui.comboBox.setCurrentIndex(1) #select by index which is zero-based self.ui.comboBox.setCurrentText("Second item") #select by text

Note that when selecting an item by text, make sure you write the correct text otherwise. The QComboBox will stay at the first element.

QTableWidget

If you want to view your database data in a tabular format, PyQt5 provides the QTableWidget for that.

QTableWidget consists of cells; each cell is an instance of QTableWidgetItem class.

Let’s design a form that contains a QTableWidget and a QPushButton. Drag a table widget and a push button from the widget box. Then save and convert the design to use it.

To add rows to the QTableWidget, you can use the setRowCount() method.

To add columns to the QTableWidget, you can use the setColumnCount() method.

from PyQt5 import QtWidgets from mydesign import Ui_MainWindow import sys class mywindow(QtWidgets.QMainWindow): def __init__(self): super(mywindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tableWidget.setColumnCount(2) self.ui.tableWidget.setRowCount(4) app = QtWidgets.QApplication([]) application = mywindow() application.show() sys.exit(app.exec())

Now you can type text manually inside QTableWidget cells.

Clear QtableWidget content

To clear QTableWidget content, you can use the clear method like this:

def clear(): self.ui.tableWidget.clear() self.ui.pushButton.clicked.connect(clear)

Populate QTableWidget by code

To fill QTableWidget programmatically, you should use the setItem() method for each QTableWidgetItem.

from PyQt5.QtWidgets import QTableWidgetItem from mydesign import * import sys data = [] data.append(('Populating', 'QtableWidget')) data.append(('With data', 'In Python')) data.append(('Is easy', 'Job')) class mywindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tableWidget.setRowCount(3) self.ui.tableWidget.setColumnCount(2) row=0 for tup in data: col=0 for item in tup: cellinfo=QTableWidgetItem(item) self.ui.tableWidget.setItem(row, col, cellinfo) col+=1 row += 1 app = QtWidgets.QApplication([]) win = mywindow() win.show() sys.exit(app.exec())

First, we create a Python list of three tuples.

Inside the constructor of the main window, we set the rows and columns count.

Then we iterate over the list and get every tuple on the list to fill the table cells using the setItem() method.

Finally, we show the main window.

Make QTableWidget not editable (read-only)

You may not like leaving your table cells editable for the user in some cases. Like showing a read-only data and any editing process makes no sense.

To make QTableWidget not editable, you can use setFlags() method to set each QTableWidgetItem not editable.

cellinfo.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) #make it not editable

You must set the flags before setting your cell content.

Therefore, your code will be like this:

from PyQt5.QtWidgets import QTableWidgetItem from mydesign import * import sys data = [] data.append(('Populating', 'QtableWidget')) data.append(('With data', 'In Python')) data.append(('Is easy', 'Job')) class mywindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tableWidget.setRowCount(3) self.ui.tableWidget.setColumnCount(2) row=0 for tup in data: col=0 for item in tup: cellinfo=QTableWidgetItem(item) cellinfo.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) # make cell not editable self.ui.tableWidget.setItem(row, col, cellinfo) col+=1 row += 1 app = QtWidgets.QApplication([]) win = mywindow() win.show() sys.exit(app.exec())

Now, if you try to edit any cell, you can’t; because the QTableWidgetItem is not editable.

Set QTableWidget column (header) name

Until now, the column names of the QTableWidget are numbers. What about setting the column names to something else.

To set QTableWidget header text, you can use setHorizontalHeaderLabels() method like this:

from PyQt5.QtWidgets import QTableWidgetItem from mydesign import * import sys data = [] data.append(('Populating', 'QtableWidget')) data.append(('With data', 'In Python')) data.append(('Is easy', 'Job')) class mywindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tableWidget.setRowCount(3) self.ui.tableWidget.setColumnCount(2) self.ui.tableWidget.setHorizontalHeaderLabels(('Column 1', 'Column 2')) # set header text row=0 for tup in data: col=0 for item in tup: cellinfo=QTableWidgetItem(item) self.ui.tableWidget.setItem(row, col, cellinfo) col+=1 row += 1 app = QtWidgets.QApplication([]) win = mywindow() win.show() sys.exit(app.exec())

The same way, you can change the row header by using setVerticalHeaderLabels() method:

self.ui.tableWidget.setVerticalHeaderLabels(('Row 1', 'Row 2', 'Row 3'))

How to sort QTableWidget

You can make your QTableWidget sortable by using the setSortingEnabled() method.

self.ui.tableWidget.setSortingEnabled(True)

Now, if the user clicks on any column header, he can sort the data in ascending or descending order.

You can use this method before or after populating the QTableWidget with data.

What about sorting the QTableWidget to a specific column only?

You can use the sortByColumn() method and set the column index and the sorting order like this:

from PyQt5.QtWidgets import QTableWidgetItem from mydesign import * import sys data = [] data.append(('Populating', 'QtableWidget')) data.append(('With data', 'In Python')) data.append(('Is easy', 'Job')) class mywindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tableWidget.setRowCount(3) self.ui.tableWidget.setColumnCount(2) row=0 for tup in data: col=0 for item in tup: cellinfo=QTableWidgetItem(item) self.ui.tableWidget.setItem(row, col, cellinfo) col+=1 row += 1 self.ui.tableWidget.sortByColumn(0, QtCore.Qt.AscendingOrder) # sort by the first column app = QtWidgets.QApplication([]) win = mywindow() win.show() sys.exit(app.exec())

Also, you can use the sortItems() method to sort QTableWidget in ascending order by default.

self.ui.tableWidget.sortItems(0)

Or you can specify the sorting order:

self.ui.tableWidget.sortItems(0,QtCore.Qt.DescendingOrder)

Keep in mind that if you want to sort your columns programmatically, you must use the sorting methods after populating the QTableWidget with data otherwise, your data won’t be sorted.

Add QComboBox in QTableWidget

You may need the user to choose a value inside the QTableWidget instead of entering a text.

What about adding a QComboBox inside QTableWidgetItem?

To add a QComboBox inside QTableWidgetItem, you can use the setCellWidget() method:

from PyQt5.QtWidgets import QTableWidgetItem from mydesign import * import sys data = ['PyQt5','Is','Awesome'] class mywindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tableWidget.setRowCount(3) self.ui.tableWidget.setColumnCount(2) row=0 for item in data: cellinfo=QTableWidgetItem(item) combo = QtWidgets.QComboBox() combo.addItem("First item") combo.addItem("Second item") self.ui.tableWidget.setItem(row, 0, cellinfo) self.ui.tableWidget.setCellWidget(row, 1, combo) row += 1 app = QtWidgets.QApplication([]) win = mywindow() win.show() sys.exit(app.exec())

Cool!

Don’t stop your mind from imagination and try to insert different widgets like a QCheckbox or even a QProgressBar.

The above code will be the same except the line where you create the QComboBox; you will add the widget you want.

The only limit is your imagination.

Packaging Python files (Converting to executable)

You can convert your Python programs into binary executables using many tools.

For me, I will use pyinstaller, which is capable of packaging or freezing Python code into executable under Windows, Linux, Mac OS X, FreeBSD, and Solaris. All this with full support for 32,64-bit architecture.

The best part about pyinstaller the full support for PyQt5.

Great! First, install pyinstaller:

$ pip3 install pyinstaller

After you installed it, you can convert your Python programs like this:

$ pyinstaller test.py

That will generate the executable on the dist folder on your Python program directory.

As you will be, a lot of dependencies generated beside the executable. What about making it one file?

You can generate one executable file using the one file mode like this:

$ pyinstaller --onefile test.py

Each time you run your executable a console window appears, what about hiding this window?

You can use -w or –noconsole to hide the console window:

$ pyinstaller -w test.py

This option for Windows and Mac OS X only.

Pyinstaller provides a lot of options to pack your app, to list all options, use –help:

$ pyinstaller --help

I tried to keep everything as simple as possible. I hope you find the tutorial useful.

Thank you.