Building a Python GUI with PyQt Designer
An effective Python + PyQt Designer workflow
Before we begin, it is assumed you have intermediate to advanced knowledge of Python. This includes basic data types (especially dictionaries), statements, and functions. Knowledge about Object-Oriented-Programming (OOP) would be useful but not required.
This will be a quick tutorial on making a Python GUI using a combination of the Python PyQt library and Designer. Designer is a program that comes with PyQt and allows you to create the GUI visually. We will use just Designer and a Python IDE/text editor. Python GUI does not need to be complicated!
Let’s look at creating a Python GUI workflow with these features:
- A configuration file for custom user settings
- Multiple windows within the program
- Adding functions to buttons, input boxes, and other clickable items
- Pictures and logos
This is really all you need for a simple but robust Python GUI. In this tutorial let’s just create a simple currency converter.
Setting up the Python Environment
We will be using Python 3.6 in this tutorial and I will be creating this tutorial in a virtual environment using Windows 10. In this tutorial we will only need to install PyQt5 for the GUI and requests for our sample project. This will be done using pip. It is important to note that Designer does not come with the pip PyQt5 installation and has to be installed separately. The code block below shows the commands that you need to type:
pip install PyQt5
pip install PyQt5Designer
pip install requests
Once it is installed, just search for “Designer” in your computer and it should pop up.
We can get free Forex data from ExchangeRate-API which conveniently has a API we can use. The API returns a JSON object with the currency information which can be navigated using a Python dictionary. The exchange rates are based on the master currency you specify.
In the example below we want to look at Forex using the US Dollar (USD) as a master currency.
import requestsurl = 'https://api.exchangerate-api.com/v4/latest/USD'
response = requests.get(url)
data = response.json()# The data variable will look like the dictionary below
"...": 7.4731, etc. etc.
Creating the GUI
Now comes the fun part. Creating the GUI!
When you open Qt Designer this prompt will open. I want to start with an empty canvas so I will choose “Dialog without Buttons” and then click “Create”.
Now you have an empty dialog window to work with. You can start to add widgets using the list on the left. You add these widgets by dragging and dropping them onto your GUI window. While there are many widgets, in this tutorial I will only cover the following widgets:
- Push Button
- Line Edit
- Combo Box
The Combo Box is also known as a drop-down list and you can populate this list by double clicking the Combo Box object in Designer, it will give you a window where you can add items. In this case, we will add the currencies we are using. This will let the user decide which currency they want to use as a reference currency.
When you start adding these widgets you should make sure that they are named properly so that you can refer to them easily in the Python code. This is done using the objectName in the Property Editor on the right. Usually I just append their function to the existing name, such as labelTitle or lineEditLocationDetails. This style of naming helps me remember what type of GUI object I’m working with when I’m typing the Python code.
Style it how you want, but for those following this tutorial create a GUI that looks like this below:
That floating “Logo” text is a placeholder for a picture that we will load. That will be explained later!
Qt Designer to Python
Save the UI file in your project folder as “mainDialog.ui”, I like to create a sub-folder called “ui” and then save it there so we can keep the project folder organized.
Once it is saved we have to convert it into something that Python actually understands. Using the command prompt, navigate to the ui sub-folder. If you’re also doing this in a virtual environment, ensure that you are also in your virtual environment that contains PyQt5 installation.
Type this code to navigate to the folder containing the ui file and convert the .ui file to a .py file:
pyuic5 mainDialog.ui -o mainDialog.py
In the root of the project folder, create a Python file called “main.py”. Input this code:
Run the main.py file using your Python interpreter. Congrats! Now Python can run the code and your GUI will pop up. I’ll explain the important parts of the code so far:
- Line 5: We are importing the mainDialog.py file from inside the ui sub-folder. I use an alias “mainDialog” to make the naming easier.
- Line 10: This class holds all the functions related to your mainDialog window which you just made. The first argument calls on the QDialog object which is the type of GUI we are using. The second argument is based on the alias we declared and it is the Ui_Dialog class inside Python file that was generated from the .ui file conversion
- Line 12: The __init__ function is important because it is the code you want to run when the program starts up. So this is where the self.setupUi(self) comes in because you want the UI to run when you open the program. We will be putting stuff like loading configuration files and button functionality here because we want them to work as soon as we run the software.
- Line 20: This is the code that executes and opens the GUI.
Adding Functions to the Buttons and Objects
Now we have our beautiful GUI. How do we make it do Forex? Let’s add the function to load the latest Forex rates which I showed in the beginning of this post. Then, let’s put in the code to connect the objects to the Python code.
When trying to connect to the objects you created in Designer, you have to start with the “self.”. So if your object in PyQt Designer was called labelTitle, you will call the object by typing in self.labelTitle. If your object was called lineEditInputDirectory then you will call it by typing self.lineEditInputDirectory and so on.
Check out the Github gist and the explanation below:
Line 16: This function is called when the user clicks the button to load Forex rates. The object itself is self.pushButtonLoadRates but we attach functionality when the user clicks it by adding .clicked.connect(). This function has some specific nuances. The argument for .connect() only accepts the function without the parenthesis, so in this case, self.load_rates. If you need to add arguments due to the requirements of the Python function you need to use a lambda, refer to the code block below:
Line 19: .currentText() gets the current string of the comboBox object which will be our reference currency. So if the user selects USD it will return Forex rates relative to 1 USD. This string is added to the URL which is used to connect to the API in the next line.
Lines 24–26: These are used to load our data onto lineEdit objects. We use .setText() to modify the text on the lineEdit objects. It only accept strings, so the numbers from the API should be converted to string.
Adding a Configuration File
This part is a little tricky and is where your existing Python knowledge will be useful. In this section we will cover the following features:
- Opening a new window within the program
- Creating and using a configuration file
Now that we have a working program, let’s say that, for the sake of an example, different people in your department have different needs and they all work with difference currencies. We can make their lives easier by adding a config file so that they don’t have to use the drop down box every time. Using a config file we can save their unique settings and then load it when the program starts up.
This is where the “Config” button comes in. Let’s create a new window for the user to modify their settings. Doing this allows us to keep the main window clean and organized.
Save this in your ui folder as configDialog.ui and as we did earlier in this tutorial, convert it using the same pyuic5 command as seen in the code block below:
pyuic5 configDialog.ui -o configDialog.py
Now here’s what the updated code should look like below. The explanations for the new code additions are below this Github gist:
- Line 6: We add the the config dialog window through this import statement
- Line 10–11: New import statements that are relevant for having a nice config file workflow. pickle and os both come with your default Python environment and do not need additional installations.
- Line 13–14: The location of the configuration file is used all throughout the script in all the classes so we declare it as a global and make it all-caps since it is a constant.
- Line 17: This class holds all relevant information for the config window. If you want to call objects in the config UI file you made it is recommended that the call is made in this class, not the MainDialog class.
- Line 23: We load the existing configuration file. We do this so that we can show what is the current setting to the user.
- Line 26: Using the loaded configuration file, we set the combo box text to whatever the current setting is.
- Line 33: When we save the config, we want to update the dictionary stored in self.config before writing the config file.
- Line 39: This closes the config parameter window.
- Line 48–54: This is the logic when loading the config file in the main dialog window. For whatever reason, the config file may go missing and then the program will not launch due to errors. If the config file is missing, create a config file with default parameters so that user can still launch the program and then modify the parameters later.
- Line 56: We load the custom settings and set the Combo Box to the user’s preferred currency.
- Line 62–63: This launches the config parameter window. This is based on the name of the config dialog class name. In this case, the class is named ConfigParameters.
- Line 65–68: After the user is done configuring their settings and the window is closed. The program will update itself and load the config file again before updating the reference currency combo box.
This workflow allows the user to save their unique settings and the program will still work if the config file is missing. You want to try and account for every problem your program will encounter.
Now you have a fully functional program, let’s add the finishing touches: a logo. While there can be a PyQt resource file where you can store images, I prefer to just load images using labels and pixmaps. This is very quick.
Line 56–58: We load the logo which I saved in the ui folder. Then I set the labelLogo object to load the pixmap and set the contents to scale to the size of the labelLogo object.
Hopefully this tutorial gives some clarity on how PyQt5 Designer works. I spent countless hours googling these things and even paid tutorials on Udemy were a bit useless.
I’m open to comments and suggestions to this workflow, I’m always trying to learn.
Full code available on Github: