4.2.1. Download, Compilation, and Running

This document guides the user on the creation of a new Python GUI Application, using Python, WAF modules and basic Taurus and Qt functionality. As this is a programming tutorial, you will produce file and code as instructued. You can also download a finalized version of the code from https://gitlab.eso.org/ecos/cut/-/tree/master/examples/, so our first step will be to download it.

$ git clone https://gitlab.eso.org/ecos/cut
$ mv cut/examples/python_application ./
$ rm -rf cut

The directory we will be using for this tutorial is the python_application, and it contains the code for a simple Python applicaiton. This is a waf project on its own, so it can be build in an independent manner from the rest of Control UI Toolkit. In order to do so, the reader must execute:

$ cd python_application
$ waf configure install

And to execute the application:

$ cutPaeGui
../_images/paegui-1.png

Python Application Example: We will create an application like this

4.2.2. Project Structure

This is the structure of the project. We use as build system WAF, and on top wtools extension to make ELT application definition more easier:

python_application/
├── PythonApplication
│   ├── src
│   │   ├── python_application_example
│   │   │   ├── applicationwindow.py
│   │   │   ├── __init__.py
│   │   │   └── mainwindow.ui
│   │   └── paegui.py
│   └── wscript
└── wscript

4.2.2.1. Project wscript

The first file we should take a look is python_application/wscript:

 1from wtools.project import declare_project
 2
 3def configure(cnf):
 4    cnf.check_python_module('PySide2', condition="ver >= num(5, 14, 1) and ver <= num(6, 0, 0)")
 5    cnf.check_python_module('taurus')
 6    cnf.check_python_module('elt.cut')
 7    cnf.check_python_module('CutWidgets')
 8    cnf.check_python_module('CiiWidgets')
 9    
10declare_project('PythonApplicationExample', '2.3.1',
11                requires=['cxx',
12                          'python',
13                          'pyqt5'],
14                recurse='PythonApplication',
15                cxx_std='c++17')
16

The syntax of this file is that of python. WAF uses several wscript files to describe its project and modules build instructions.

The project is named PythonApplicationExample, and this file is in charge of setting up dependencies. Here we check for the presence in our development environment of any required library or python module for the compilation and execution of our application. In particular, we check for:

  • PySide2, which is the python bindings for Qt

  • taurus

  • elt.cut

  • CutWidgets

  • CiiWidgets

The declare_project() method in line 10 includes under the require keyword the python and pyqt5 strings, which indicates that the included modules could be python programs.

Then, the recurse list indicates which directory will be considered as module for this project. This WAF project has only 1 WAF module: PythonApplication.

The PythonApplication WAF module is the most important one. To create a basic UI application, we need the following files and directories.

  • One python package: the directory PythonApplication/src/python_application_example

  • Its __init__.py file

  • At least one UI file mainwindow.ui,

  • The implementation of said UI file applicationwindow.py

  • An entry point script paegui.py

  • One wscript that handles the module.

4.2.2.2. Module wscript

python_application/PythonApplication/wscript:

 1#!/usr/bin/env python3
 2from wtools import module
 3
 4# The declare_pyqt5program instruction will automatically find
 5# the QT Resources, and the UI files, and run the necessary
 6# commands to produce the python versions of them.
 7
 8module.declare_pyqt5program(
 9    target='cutPaeGui',
10    py_program="src/paegui.py",
11    py_package="src/python_application_example",
12    use=[
13        "cut.widgets.bindings",
14        "cut.task",
15    ]
16)

This file uses the declare_pyqt5program directive to instruct WAF that this module produces an executable GUI application.

In line 9 the reader may find the target argument, which tells the name of the executable. Independent of the py_program, when installing, the py_program is installed using the target name into the $PREFIX/bin/ directory.

If the python application needs a package to be installed, the reader may specify it using the py_package argument. This will be installed as a python package under $PREFIX/lib/python$PYTHON_VERSION/site-packages/} directory.

The use argument is to provide a list of dependencies this module has. Most importantly, these dependencies will be used also during tests.

For more details and information on wtools and WAF, please refer to their manuals.

4.2.3. Entry Point Python Script

The entry point python script paegui.py is the one that creates a Qt GUI Application.

13 import sys
14 import click
15 import taurus
16 import taurus.cli.common
17 from taurus.qt.qtgui.application import TaurusApplication
18 from paegui import ApplicationWindow

taurus.qt.qtgui.application.TaurusApplication inherits from QApplication, and it is in charge of application initialization, configuration of default values, logging, etc.

In this example, three taurus utilities are used:

  • taurus.cli.common provides common option parsers to both custom application and Taurus

    internal tools. It is based in click and in this case, the log_level option is used. It will ensure that the format that we use to ask for the log level is the same through all applications.

21 @click.command("paegui")
22 @taurus.cli.common.log_level
23 def paegui(log_level):
24     ...

The code above is the beginning of the paegui python method, which is the first one invoked after the python intepreter enter the __main__ application entry point.

Since the parser for log_level is already taken care of, the developer is free to use it where they want. In this case the example forwards the log_level value to the method that sets the root logger’s level. See line 34.

28    app = TaurusApplication(
29        app_name = 'Python Application Example GUI',
30        app_version = '0.0.1',
31        org_name = 'ESO',
32        org_domain = 'eso.org',
33        )
34    taurus.setLogLevel(getattr(taurus, log_level))
35    window = ApplicationWindow()
36
37    window.show()
38    sys.exit(app.exec_())
39
40
41 if __name__ == "__main__":
42    paegui()

The last section of the main method is listed above. The most important part is the declaration of app, as a taurus.qt.qtgui.application.TaurusApplication in line 28. This will create a context where Qt and Taurus objects can exists.

In line 40 the user interface is instantiated by the creation of a ApplicationWindow object, which is then assigned to the window variable. The ApplicationWindow class is part of the examples python package, and contains the implementation of the UI.

At this point, the application is ready to start, and to do so, we order it to show something in the screen. In this case, the GUI we have just loaded, using the show() method.

But control is still under the script’s flow; developer most surrender control of the program, to Qt’s event loop. A program or a python script is a sequential flow of instructions. There can be decision points where the program flow to one section of the code to another, but in general terms, is a linear flow.

When we surrender control of the program to Qt event pump, Qt will constantly look in the system for inputs like keystrokes and mouse movements and click, and execute the appropriate methods. We have in fact changed from a sequential program, to an event-based one. Events are the deciding factor of the programs next instruction set.

To pass the control to the event pump, we invoke app.exec_() method. This will leave the application in an endless loop governed by events.

See Definitions for more details on Asynchronous programming, Events, and Qt’s Event Engine.