Introduction

There are several options to develop GUI apps in Python. One of these is with the Qt cross-platform framework, and the PyQt bindings. One can find a decent tutorial for using PyQt with Python here: http://zetcode.com/gui/pyqt5/.

Developing the entire UI for your application by writing out the code to lay out all the controls is painstakingly slow. Instead, one can use Qt Designer, which is part of the Qt Creator IDE for Qt. But the IDE is designed for C++, not Python. This post explains how to integrate Qt Designer with PyQt 5. It assumes that you already have both these installed on your system.

About .ui files

Qt Designer uses .ui files to represent the GUI. These files are just XML files with information about the window layout. We’d like to be able to somehow access these files from Python and render them with Qt. Below is an example of such a file, which you might want to use to follow along.

<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow" >
  <property name="geometry" >
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle" >
   <string>Hello, world!</string>
  </property>
  <widget class="QMenuBar" name="menuBar" />
  <widget class="QToolBar" name="mainToolBar" />
  <widget class="QWidget" name="centralWidget" />
  <widget class="QStatusBar" name="statusBar" />
 </widget>
 <layoutDefault spacing="6" margin="11" />
 <pixmapfunction></pixmapfunction>
 <resources/>
 <connections/>
</ui>

There are a few ways to do this. One way is to include the file with your app, and parse and render it at runtime using a library. Another way is to compile them into Python classes ahead of time, and just include those Python source files with your app. I used the second approach.

To do this, I used a package called pyqt-distutils. This package lets you build your .ui files into Python classes as part of setup.py. The idea is to keep the .ui files alongside their built .py files.

To set up my directory structure, I issued these commands:

$ mkdir -p myapp/gui
$ touch setup.py myapp/{__init__,__main__}.py myapp/gui/__init__.py
$ pbpaste >myapp/gui/mainwindow.ui  # paste in the above .ui file
$ tree
.
├── myapp
│   ├── __init__.py
│   ├── __main__.py
│   └── gui
│       ├── __init__.py
│       └── mainwindow.ui
└── setup.py

2 directories, 5 files

Setting up pyqt-distutils

First, install the package:

$ pip install pyqt-distutils

Now, we need to make a configuration file. Fortunately, the package comes with a command-line utility to help us. Create the configuration file:

$ pyuicfg -g --pyqt5
pyuic.json generated
$ cat pyuic.json
{
    "files": [],
    "hooks": [],
    "pyrcc": "pyrcc5",
    "pyrcc_options": "",
    "pyuic": "pyuic5",
    "pyuic_options": "--from-import"
}

Next, we need to add the .ui files to be built. Update pyuic.json to have this entry in files:

{
    "files": [
        [
            "myapp/gui/*.ui",
            "myapp/gui"
        ]
    ],
    "hooks": [],
    "pyrcc": "pyrcc5",
    "pyrcc_options": "",
    "pyuic": "pyuic5",
    "pyuic_options": "--from-import"
}

The first entry in the sublist is the file to build, and the second entry is the directory to put the built file in. In my case, I just stored the built files right alongside the .ui files.

Finally, we need to add a command to actually build the .ui files. This is the suggestion from the documentation, to be added to setup.py:

from setuptools import setup

try:
    from pyqt_distutils.build_ui import build_ui
    cmdclass = {"build_ui": build_ui}
except ImportError:
    cmdclass = {}

setup(
    name="myapp",
    version="0.1",
    packages=["myapp"],
    cmdclass=cmdclass,
)

Run the build_ui command:

$ python setup.py build_ui
running build_ui
pyuic5 --from-import /Users/waleed/Workspace/python/pyqt-test/myapp/gui/mainwindow.ui -o /Users/waleed/Workspace/python/pyqt-test/myapp/gui/mainwindow_ui.py
$ tree
.
├── myapp
│   ├── __init__.py
│   ├── __main__.py
│   └── gui
│       ├── __init__.py
│       ├── mainwindow.ui
│       └── mainwindow_ui.py
├── pyuic.json
└── setup.py

2 directories, 7 files

As you can see, it generated the mainwindow_ui.py file. Since this is generated code, you may want to add it to your .gitignore or similar.

Launching the app

Once the .ui files are built, you need to import the created classes into your own code to use them. Put this content into myapp/__main__.py:

import sys

from PyQt5.QtWidgets import QApplication, QMainWindow

from .gui.mainwindow_ui import Ui_MainWindow

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

Install your app and launch it:

$ ls -1  # should be in the directory with setup.py
myapp
pyuic.json
setup.py
$ pip install -e .
Obtaining file:///Users/waleed/Workspace/python/pyqt-test
Installing collected packages: myapp
  Running setup.py develop for myapp
Successfully installed myapp-0.1
$ python -m myapp
The working, wholly featureless Qt app.

The working, wholly featureless Qt app.

Using Qt Designer

Once all this is set up, we just need to use Qt Designer to modify the .ui file for us, so that we can rebuild it and have it appear in our app.

Launch Qt Creator, click File > Open File or Project..., and select your .ui file. (Do not try to create a project of any sort.) It should appear in Qt Creator for you to edit and save. That’s it!

The welcome screen for Qt Creator.

The welcome screen for Qt Creator.

Qt Designer's UI editor.

Qt Designer’s UI editor.