Introduction

In my tutorial on Python GUI's with PyQt, I had many people bring up the fact that when modifying the GUI, as soon as pyuic5 is executed again to rebuild the Python file, all original changes will be lost.

In a sense, this is true. For demonstration proposes, I had put all code into the Python file generated, but a smarter way to add code would be have been to import the generated file so that when it changes (executed pyuic5 again to create an updated .py file), it would only have affected the imported file. This method also allows for separation of the GUI and logic.

In this tutorial, I am going to cover a method that allows you to import the .ui file generated by PyQt Designer directly in Python. Please be aware that there is a lot more effort when importing it this way and it can be a lot harder to find where errors are occurring.

Installing PyQt5

Go to my previous tutorial to learn how to install PyQt5. Generally, you can install it using python -m pip install pyqt5 regarding your environment is set up correctly.

If you haven't got the designer, you can use python -m pip install pyqt5-tools to install tools that contain the designer. Finding the executable can be a bit tough to find if you don't know where packages install so I would recommend reading the other post to help you find it.

Generating the UI File

As covered in my original PyQt5 tutorial, install the designer, locate it and use it. When saving the GUI you have created, it will be saved as a .ui. This .ui file is XML that contains the layout of the GUI and other things you may have set in the designer application. Here is a snippet of an example .ui file:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
    <class>MainWindow</class>
    <widget class="QMainWindow" name="MainWindow">
        <property name="geometry">
            <rect>
                <x>0</x>
                <y>0</y>
                <width>367</width>
                <height>339</height>
            </rect>
        </property>
        <property name="windowTitle">
            <string>MainWindow</string>
        </property>
        <widget class="QWidget" name="centralwidget">
            contents...
        </widget>
    </widget>
    <resources/>
    <connections/>
</ui>

Regarding you know what XML is, this is pretty basic; knowing a little bit of XML is required for this.

Importing the UI File In Python

First we need to import the modules required. We need QtWidgets from PyQt5 for the base widget and uic from PyQt5 also to load the file. We also need sys to access arguments.

from PyQt5 import QtWidgets, uic
import sys

Next we need to create a base class that will load the .ui file in the constructor. It will need to call the __init__ method of the inherited class, load the .ui file into the current object and then show the window.

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__() # Call the inherited classes __init__ method
        uic.loadUi('basic.ui', self) # Load the .ui file
        self.show() # Show the GUI

It is very important here to inherit the correct class. In this example I inherit QtWidgets.QMainWindow because I created a new "Main Window" when selecting the form type when first creating the .ui file in PyQt Designer. If you look back to the source of the .ui file, we can actually identify the class we need to inherit.

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
    <class>MainWindow</class>
    <widget class="QMainWindow" name="MainWindow">
        <property name="geometry">
            <rect>

This is a snippet of the XML from before. You can see "MainWindow" is the root widget as all the content is wrapped in the <widget class="QMainWindow" name="MainWindow"> ... </widget> element. This tag has a class attribute; in this example, the value is QMainWindow, which explains why I used QtWidgets.QMainWindow.

Your widget class may be different so be sure to double-check!

After this, we then need to create an instance of this class we just made and execute it.

app = QtWidgets.QApplication(sys.argv) # Create an instance of QtWidgets.QApplication
window = Ui() # Create an instance of our class
app.exec_() # Start the application

Putting this all together, we get:

from PyQt5 import QtWidgets, uic
import sys

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi('basic.ui', self)
        self.show()

app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()

This assumes that the .ui file is called basic.ui

Run the script to make sure everything runs, if the GUI that you created appears, congratulations! If not, look back over what you may have missed and READ THE ERROR (I cannot stress this enough).

A small check-list for things that may go wrong:

  • PyQt5 isn't installed
  • The .ui file you are importing does not exist (incorrect reference)
  • You did not inherit the correct class (found in the XML)

Getting Widget Object Pointers

Once you have the GUI being imported, you now need to identify some pointers for the objects you want to use. For this example I am going to use the .ui linked below:

Download: basic.ui

basic.ui screenshot

This GUI contains 5 widgets that we can see:

  • Text input line
  • A button to the right of this saying "Print Content"
  • 3 buttons below the input saying "Mode", "Set" and "Clear"

When looking at the XML, we can see that there is a centralwidget inside a QMainWindow and inside the centralwidget are the 5 widgets I created.

Giving Widgets Unique Names to Find Them With

The most important part of getting a pointer to one of these widgets is to give each widget a unique name, preferably something that is friendly to read. Open my .ui file in the Designer or in notepad to see the names I have given each widget. In the XML you can see the 5 widgets I created have friendly name attributes; these names can help us identify the widgets.

To set these names, when clicking on an object in the designer, the property editor on the left provides a field called objectName

objectName in the designer

Set this to what you want the object to be called and you should see this in the XML when you save the file.

You don't have to look at the XML, seeing/modifying it in the designer is enough.

Using These Names to Find the Widgets

Now that each widget has a name attribute, we can get pointers of these objects. When I say pointers, I mean a variable that we can use to access this widget and modify it.

When uic.loadUi('basic.ui', self) is called, the names of the widgets will be used to create pointers to the widgets themselves. This means to get our button named "printButton", we can access it using self.printButton within the same class that uic.loadUi('basic.ui', self) was called in after it has been called (technically we can call that on whatever self is).

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi('basic.ui', self)

        # Set the print button text to "Text Changed"
        self.printButton.setText('Text Changed')
        # This should not throw an error as `uic.loadUi` would have created `self.printButton`

        self.show()

Searching For A Pointer

In the case that the method above doesn't work for some widgets, you can still search for them. To find an object, we can use findChild on any one of its parent objects while supplying the type of widget we are getting and the name.

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi('basic.ui', self)

        # Find the button with the name "printButton"
        self.button = self.findChild(QtWidgets.QPushButton, 'printButton')
        # We have now created `self.printButton` ourselves (will overwrite whatever was there if something existed already)

        self.show()

Alternatively, you can leave the name out to find the first object of the type you provided. You can also call findChildren to find more than one object.

In the example above, I have searched for a QPushButton object with the name "printButton". To find the object type you need to search for, each widget in the XML will have a class attribute beside the name attribute. If you look at the .ui file I provided and search for "printButton", you will see an attribute class on the same line with a value QPushButton; this is how I knew to use QtWidgets.QPushButton.

<widget class="QWidget" name="centralwidget">
    <widget class="QPushButton" name="printButton">
        <property name="geometry">

To find the name of this class in the designer, click on a widget and look at the "Object Inspector" window to the right. In the image below you can see that the type of the widget named "input" is QLineEdit; so I would then use QtWidgets.QLineEdit.

widget class name in the designer

The method above simply helps you get a pointer to the object. Now that the hardest part is done, you are free to do what you would normally do after you have located all your widgets you want to use.

Please note, this is not a full tutorial on PyQt5. I am simply demonstrating how to import .ui files

Connecting Buttons to Methods

To connect a button to a method, we need to get a pointer (as shown before) and then connect it like we normally would. Here is a full example:

from PyQt5 import QtWidgets, uic
import sys

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi('basic.ui', self)

        self.button = self.findChild(QtWidgets.QPushButton, 'printButton') # Find the button
        self.button.clicked.connect(self.printButtonPressed) # Remember to pass the definition/method, not the return value!

        self.show()

    def printButtonPressed(self):
        # This is executed when the button is pressed
        print('printButtonPressed')

app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()

Reading Inputs

To read inputs, I will use the button method from before to trigger an event and also get another pointer to the input widget.

from PyQt5 import QtWidgets, uic
import sys

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi('basic.ui', self)

        self.button = self.findChild(QtWidgets.QPushButton, 'printButton') # Find the button
        self.button.clicked.connect(self.printButtonPressed) # Remember to pass the definition/method, not the return value!

        self.input = self.findChild(QtWidgets.QLineEdit, 'input')

        self.show()

    def printButtonPressed(self):
        # This is executed when the button is pressed
        print('Input text:' + self.input.text())

app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()

These are only the basics but are great to build up from.

This Seems Like A Lot Of Effort?

Yes, at first it will be a lot of effort, but once you have set up all the pointers, you no longer need to worry about the GUI changing (regarding you keep the names of widgets the same). This is a small price to pay in 'overhead' for more smooth development later.