There are interesting use cases when you can interact from remote with an application, for example remote assistance.

Problem statement

Suppose you are in the first phases of the project in which the development team is located far from the design team, that doesn’t have the knowledge and the software needed to build the application. You need to do fast iterations with the design team, possibly without sending them any hardware or installing any development tool. What could you do?

The Qt framework has evolved over the years many options to interact with an application from remote. As of today the following options are available:

Both VNC and WebGL are QPA plugins, so they can be used with any existing application.

You can use the VNC plugin with the command line option -platform vnc. This plugin will spawn a VNC server at startup and you can connect to the server to interact with the application.

Likewise, you can use the WebGL plugin with the option -platform webgl. This plugin will spawn a minimal web server serving an HTML page that will draw your application through OpenGL commands.

Finally, the WebAssembly option makes our application work from remote. It is not a platform plugin. So, it requires quite a lot of changes on your codebase.

Let’s briefly review the pros and cons of each solution:

VNC

WebGL

WebAssembly

In this article I will focus on the WebGL solution because it provides a way to remote the application on a browser, which allows you to connect with a pc, a smartphone or a tablet without installing additional software.

Multi-user support

If you already tried the WebGL plugin (using -platform webgl), you probably noticed that it supports only one user at the time. This shouldn’t be unexpected, since Qt UIs are natively single user.

You can overcome this limitation without rewriting a good part of your application by spawning more processes, one for each user, in which the state is a mirror of a master application.

Qt Remote Objects

Qt Remote Objects is a Qt module that allows you to mirror any QObject or QAbstractItemModel between processes. It is not a generic IPC mechanism, but it is enough to replicate the state of any Qt Quick application.

The main concepts exposed by the Qt Remote Objects module are:

Node

The processes that use Qt Remote Objects form a network in which there are two types of nodes: the Host nodes contain Source objects that are shared, the Client nodes acquire one or more Replicas of a Source object.

These networks are peer to peer, so every Client node must have a connection with the Host node that contains the required source.

Source

Sources are instances of QObject or QAbstractItemModel that we want to share with other processes. The Source instance lives in a Host node and must be explicitly shared by the node before it is visible on the network.

Sharing an object is straightforward:

ControlUnit *o = new ControlUnit; // create your object as usual
QRemoteObjectHost srcNode(QUrl("local:ControlUnit")); // set the node address
srcNode.enableRemoting(o, “MyControlUnit”); // set object name on the node

Replica

Replica objects are proxies for Source objects inside the client process.
Such objects have the same interface as the original objects, so they can be directly used in QML.
You can use the following code to acquire a Replica for an object:

QRemoteObjectNode repNode;
repNode.connectToNode(QUrl("local:ControlUnit")); // connect to host node
QSharedPointer<QRemoteObjectDynamicReplica> ptr; // hold the replica
ptr.reset(repNode.acquireDynamic("MyControlUnit")); // acquire object by name
// The replica is ready after initialized()
//connect(ptr.data(), SIGNAL(initialized(), this, SLOT(replicaReady()));

But if you want to acquire a Replica for a model you need to use a slightly different code:

QRemoteObjectNode repNode;
repNode.connectToNode(QUrl("local:ControlUnit")); // connect to host node
QSharedPointer<QAbstractItemModelReplica> repPtr;
repPtr.reset(repNode.acquireModel("MyModel"));

C++ API for shared objects

The above examples work well as long as we use the acquired object in QML, but they are awkward to be used from C++ because we don’t have the API for the object. This means that models acquired through acquireModel() have access to the API of QAbstractItemModel only; additional signals or slots are not available on the replica. This is the price we pay to use a generic function like acquireDynamic().

You can generate type information with another tool, named repc (REPlica Compiler) that creates a C++ file with type information about your object.

For example, let’s suppose you want to add a method named addItem(QString) to our model. The first step is to write a file .rep describing the API:

class MyModel
{
	SLOT(void addItem(const QString &name));
};

Then, we can add this file to the REPC_REPLICA variable inside our QMake project:

REPC_REPLICA = myModel.rep

The repc compiler creates a class that we can use to export the model API:
Source node:

srcNode.enableRemoting(&model, "ModelAPI")

Replica node:

QSharedPointer<MyModelReplica> ptr(repNode.acquire<MyModelReplica>("ModelAPI"));
engine.rootContext()->setContextProperty("modelAPI", ptr.data());

In the end we need to export two objects to the QML engine, the first is the data model itself and the second is the object that provides additional APIs for that model.

Conclusions

You have seen how it is possible to synchronize QObjects and Qt models between processes with few lines of code and how you can mirror the UI on any browser using the WebGL plugin. Using this setup you can interact with the remote application as if the application was local.

There are many more applications of Qt Remote Objects. Here are some use cases that you can find in the documentation: