Declaration of VAR

and some other stuff

TCP client-server applications with Qt Quick / QML

2018-03-08 11:56:15 +0100

2018-03-08 11:56:15 +0100 | Comments

More than an year ago I wrote an article about C++ backend for QML, showing a very basic example of interaction between QML and C++. If you don’t know anything about C++/QML interaction, I recommend you to read that one first.

Today I want to revisit the subject and provide a more sophisticated example - two applications with network communication between them (client and server).

Even today, in 2018, I keep seeing a decent level of confusion about Qt Quick/QML. Quite often people just don’t believe/aware that Qt Quick/QML applications can be as capable as the ones based on Qt Widgets.

The most common argument I hear against Qt Quick/QML is that “JavaScript cannot do as good job as C++”. Certainly it is so, and I can’t agree more, but who forces you to implement everything in JavaScript? Actually, that is a bad practice, and it is clearly advised against - Qt Quick/QML should only be responsible for GUI so it is your frontend, and all the implementation and heavy stuff should happen on C++ side which is your backend. Yes, I use “frontend” and “backend” terms even though both QML and C++ stuff are inside the same application.

Conveniently enough, recently I was asked if it is possible to implement a GUI-application based on Qt Quick/QML that would be able to interact with other applications over the network. To answer this question I have prepared a proof of concept demonstrating that it definitely is possible, and I would like to share this demo with you.

So, architecturally a Qt Quick/QML application consists of:

  1. Frontend, Qt Quick/QML;
  2. Backend, C++.

Let’s start with backend. In our case, it implements QTcpServer and QTcpSocket network communication. So, there is a server application and there is a client application. Server application has QTcpServer and it listens to some port. Client has QTcpSocket, but has not connected to the server yet:

When client connects to server, a QTcpSocket is created on the server’s side, through which server and client can talk to each other:

There can be even several clients - server will create a separate socket for each one of them:

I won’t show the code for QTcpServer/QTcpSocket implementation here as it is pretty trivial and is not the point anyway, but if you are interested you can look at it later in the project repository.

Now, how to “get” all that from C++ to QML, i.e. how frontend would know about any of this? Well, first, we need to determine what exactly do we want to pass between frontend and backend.

Let’s take the case of the client application (as it is a more interesting one). Here’s how it looks like:

So, that’s what we need:

  1. Naturally, process onClick events for the buttons (connect to server, disconnect from server, send the message);
  2. Display the current status of the connection and get notified when it changes;
  3. Get notified when a new message has been received and display it;
  4. Get notified when some error has occurred and display it.

As you can see, we let the backend to handle the core functionality (establishing connections, sending/reading the data, etc), and we only need it to provide an interface for getting the things listed above.

Buttons onClick events are merely C++ function/methods calls (that was described in the previous article), and notifications are done via Q_PROPERTY and hooking up to signals. Schematically all that can be represented like this:

And below you can see all these slots, signal and functions in the actual code fragments.

C++ backend:

class Backend : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool currentStatus READ getStatus NOTIFY statusChanged)

public:
    // ...
    bool getStatus();

signals:
    void statusChanged(QString newStatus);
    void someError(QString err);
    void someMessage(QString msg);

public slots:
    void setStatus(bool newStatus);
    void receivedSomething(QString msg);
    void gotError(QAbstractSocket::SocketError err);
    void sendClicked(QString msg);
    void connectClicked();
    void disconnectClicked();

// ...
};

QML frontend:

// ...

Backend {
    id: backend

    // to hook up to some signal from C++ side you just need to add "on" to its name
    onStatusChanged: {
        // ...
    }
    onSomeMessage: {
        // ...
    }
    onSomeError: {
        // ...
    }
}

// ...

Rectangle {
    // ...
    color: backend.currentStatus ? "#CAF5CF" : "#EA9FA9"

    Text {
        id: status
        text: backend.currentStatus ? "CONNECTED" : "DISCONNECTED"
        // ...
    }
}

// ...

Button {
    text: "Connect to server"
    onClicked: {
        backend.connectClicked();
    }
}

Button {
    text: "Disconnect from server"
    onClicked: {
        backend.disconnectClicked();
    }
}

// ...

Button {
    text: "Send"
    onClicked: {
        backend.sendClicked(msgToSend.text);
    }
}

Basically, that’s it, but I guess I should explain how Q_PROPERTY works. We used it in this code fragment:

Q_PROPERTY(bool currentStatus READ getStatus NOTIFY statusChanged)

Here we declare for QML side that the Backend object will have a property named currentStatus. But just declaring its name is not enough, because there should be also a READ method for getting its value from C++ side and a NOTIFY signal for notifying QML side when it changes:

If you won’t have those, then your frontend won’t know when the currentStatus changes and to what value. In our case, depending on this C++ property value, QML label status changes its text (CONNECTED, DISCONNECTED) and background color (green, red).

To show you the final result, I made video of both client and server applications at work:

By the way, I created almost the same demo about 5 years ago with Qt Widgets, so for me it was even more interesting to “port” it to Qt Quick - splitting it into frontend and backend.

When you work with Qt Widgets everything is in C++ already, so there is no need to do this QML-C++ interactions via injected backend objects. That actually begs the question: why even bother, why not to do every project with Qt Widgets, because after all it is easier that way? Well, a bit easier, yes, but take a look on the following screenshots and tell me, which GUI you like more, this one (Qt Widgets):

or this one (Qt Quick/QML):

Yeah, I know, the second one is almost the state of art. And Qt Quick/QML applications can be much fancier, which definitely is not the case with Qt Widgets. I can’t even begin to estimate the amount of work that needs to be done with Qt Widgets in order to get something as nice looking.

The full source code for both Qt Quick/QML and Qt Widgets demos is available here.