Embedded Approaches with QT/QML

Developers of GUI based systems, using QT, are faced with consistent challenges:

This paper focuses on some approaches to help the developers approach the implementation strategy in a way that allows clean development, debug and deployment of complex products. The concepts outlined in this paper provide the following:

Python as a Development Tool

Although most people will not deploy a product that is completely Python based, Python serves as a excellent choice for easing the development cycle for the following reasons:

In summary, it's ease of use, and the abundance of online documentation make it an excellent choice as a development tool, as scripting is rapidly prototyped, tested and deployed, and it's core is string based operations, making it easy to interact with.

QT and QML

Although QT has been around for many years, and there are many product GUI's developed around it, QML is a relatively new player in recent years. The advantages of a mixed-mode GUI development environment are the following:

If QML is adopted as the primary language for development of the GUI, then the underlying QT, C++ based logic, simply serves as a foundation for the QML. Core services in QT/C++ can be developed that operate under Windows or Linux, and then stable, rarely requires change, while the bulk of the GUI development efforts lay in QML.

A Practical Implementation Strategy

Figure 1 serves as a foundation for discussion of a practical implementation strategy.


Figure 1 ‒ A Practical Implementation Architecture

GUI Core

The GUI Core is designed to operate under both Windows and Linux. It's foundational design is rooted around socket level communications with some connected server. Thus, it is a “client” in a Client/Server model, and operates stateless. There are really two modes of development under QT:

In either case, the point is to abstract these away from binding with underlying core application services. However, the preferred approach is QML, with Objects by name, versus the traditional Single and Slots model.

In the architecture showing in Figure 1, we have two main components, with three key sub-components:

QML Object Manager

A C++ set of methods responsible for providing a “bridge” between QML and the connected Server that provides functional, state-full services. Listing 1 shows a simple button that requests a “load” of some data:


    ExmplButton1 {
        buttonText: "Load" 
        id: exmpl_button_Load 
        x: 550 
        y: 530 
        MouseArea { 
            anchors.fill: parent 
            hoverEnabled: true 
            acceptedButtons: Qt.LeftButton | Qt.RightButton 
            onClicked: { 
                info.text = 'Clicked (wasHeld=' + mouse.wasHeld + ')' 
                var exmplString = "get exmplButton" 
                exmpl.updateField( exmplString ) 
            } 
        }

Listing 1 ‒ Simple Button to Query

This example has two characteristics:

>
    ExmplButton1 { 
        QObject *qmlTextBox = dataPagesObject->findChild(objectId);   
        if( qmlTextBox ) { 
            qDebug() << "ExmplGui::sessionProcessReadData(): Object Found";
            qDebug() << "Setting Text";
                qmlTextBox->setProperty( "text", QVariant( objectVal ) );
        }
    }

Listing 2 ‒ Back Binding to QML by ID

Although specific details are omitted from these listings, there are two key points:

  1. Strings passed down from QML contain an Object ID encoded in them. In this case, it is “exmplButton”.
  2. Strings returned by underlying services contain that Object ID encoded in them, thus allowing a reverse lookup from the QT C++ services to find the original Object ID, and make changes to the attribute based on specific request. In this case, the underlying, remote connected server understood from the passed string to have QT C++ services lookup by name “exmpl_button_load”. Specifically, the QT C++ looked up the QML Object, by ID, through findChild(objectId).

Example String/Object ID Bindings

On some sort of query from QML to the underlying application code, and takes the form of:

get objectId=[optional parameters]

On some sort of “set”, which causes action to happen from the underlying application code, the string takes the form of:

set objectId=[parameters, comma separated, which are parsed by underlying application code]

Return strings adhere to the rules, providing easy back binding to original request. The result of this is:

The GUI operates independent of the application code, the application code operates independent of the GUI.

Python, Our Development Friend

The foundation of this approach is how to employ Python to help with development efforts. Python is a friendly language, that is string based, and allows easy Client/Server interactions. This paper has focused on string based operations. This is key to utilization of Python to help us develop:

An easily used language for rapid-prototype work, and gives simplistic approaches to developing both the GUI and application code independent of each other.

Summary

All products contain unique challenges on how they are developed and deployed. This paper offers an approach that allows simultaneous development of a GUI and Application Code while working with the realities of daily life; multiple groups involved, hardware in flux, and integration always being the challenge for final deployment.

It's core concept is design to debug, and design to have clean interfaces without the complicated bindings that prevent unit testing and clean deployment. Often it is easier to develop a GUI under and Windows based host, without target hardware, while the application code is developed on the target hardware.