• +43 660 1453541
  • contact@germaniumhq.com

How to Create Qt5 Python Applications Using PySide2


How to Create Qt5 Python Applications Using PySide2

When doing GUIs in Python, I always see tutorials on either TkInter or PyQt5. Most people don’t realize that "The Qt Company" (that’s their name, really) has their own API, called PySide2, and it’s trivial to use. Let’s explore how.

For this tutorial, I will assume Ubuntu 20.04 is the OS, but the steps are generally the same. First, we’ll install qttools5-dev-tools. This package provides us the Qt Designer, so we’re not designing forms from code:

sudo apt install qttools5-dev-tools

With this UI designer, we’re able to create main windows, dialogs, and custom widgets. For the sake of this tutorial, we’ll do a window and two widgets to illustrate. We’ll open the UI designer by calling the designer, and visually edit our widgets. We’ll save them as: main.ui, the main window, event.ui and task.ui.

Designer

Then, we’ll install PySide2, the Qt5 API Python bindings:

pip install -U PySide2

This package also provides a compiler that will translate the *.ui files into Python source files. We’ll use it to create the Python sources from our previously defined .ui files.

mkdir -p adhesive_event_debugger/ui/
pyside2-uic ui/main_window.ui > adhesive_event_debugger/ui/main_window.py
pyside2-uic ui/event_widget.ui > adhesive_event_debugger/ui/event_widget.py
pyside2-uic ui/task_widget.ui > adhesive_event_debugger/ui/task_widget.py

Note: Don’t use these generated sources directly, since they are just the scaffolding of the layout. We’ll inherit from them instead. So let’s create our implementing classes first:

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self) -> None:
        super(MainWindow, self).__init__()

        self.setupUi(self)

        self.add_items()

    def add_items(self):
        self.add_event_generators()
        self.add_active_events()

    @mopyx.render
    def add_event_generators(self):
        clear_layout(self.messages_group_box)  # (1)
        for event_generator in model.instance.event_generators:
            self.messages_group_box.addWidget(EventGeneratorWidget(
                task_id=event_generator.task_id,
                task_name=event_generator.task_name,
            ))

    @mopyx.render
    def add_active_events(self):
        clear_layout(self.running_group_box)  # (1)
        for active_event in model.instance.active_events:
            self.running_group_box.addWidget(TaskWidget(
                event_id=active_event.event_id,
                task_name=active_event.task_name
            ))
  1. UI components are available on the self instance of the window/widget.

And a widget (the other one is virtually the same):

class EventGeneratorWidget(QWidget, Ui_Form):
    def __init__(self, *, task_id: str, task_name: str) -> None:
        super(EventGeneratorWidget, self).__init__()

        self.task_id = task_id

        self.setupUi(self)
        self.label.setText(task_name)

        self.send_button.clicked.connect(self.send_clicked)  # (1)

    def send_clicked(self) -> None:
        oaas.get_client(AdhesiveProcess).generate_event(self.task_id)
  1. As you can see we connected a signal, so we have some functionality when pressing the button.

We wire up everything into the Qt’s event loop:

def main():
    app = QApplication(sys.argv)

    main_window = MainWindow()
    main_window.show()

    sys.exit(app.exec_())

When running it we should see this awesome window:

Event Debugger

Note
I’m using mopyx, a reactive framework I wrote to deal with automatic rerendering of the components when the underlying model changes.
Note
I’m also using oaas (Operation as a Service), another framework I wrote to deal with remote calls.

The full source code for the project is available here https://github.com/bmustiata/adhesive-event-debugger

Enjoy :)