Creating a Multi-Page QML Application with StackView and Dynamic Titles

This tutorial gives you some perspective about how to use StackView to navigate between pages with dynamic titles. You can find the complete project here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import QtQuick
import QtQuick.Controls

ApplicationWindow {
//title: currentItem.title
width: 640
height: 480
visible: true

StackView {
id: stackView
initialItem: mainPage
anchors.fill: parent
onCurrentItemChanged: {
title = currentItem.title;
}
}

Component {
id: mainPage

Page {
title: qsTr("Main Page")

Column {

Button {
text: "Push Page 1"
onClicked: stackView.push(page1)
}

Button {
text: "Push Page 2"
onClicked: stackView.push(page2)
}
}
}
}

Component {
id: page1

Page {
title: "Page 1"

Button {
text: "Pop"
onClicked: stackView.pop()
}
}
}

Component {
id: page2

Page {
title: qsTr("Page 2")

Button {
text: "Pop"
onClicked: stackView.pop()
}
}
}
}

TCP Socket Communication using C++, Qt 6 and QML

In this post we are going to see how to build a TCP socket communication. This idea came to me from the example of Qt 6 C++ GUI Programming Cookbook book. You can find this demo on chapter 7 “Using Network and Managing Large Documents”. In this chapter the client UI is built using Widgets but in this demo we are going to use QML. We will use the TCP server from the book for this demo and we will only build the TCP client.

TCP Server

You can find the TCP server implementation in this link. Furthermore, you will find the TCP client implementation and FTP implementation (which we aren’t going to discuss in this article).

TCP Client

In this repository you will find my TCP client implementation using C++ and QML.
This is the header file of TCP client implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#ifndef CLIENTCONNECTION_H
#define CLIENTCONNECTION_H

#include <QObject>
#include <QQmlEngine>
#include <QString>
#include <QDebug>
#include <QTcpSocket>

class ClientConnection : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged)
Q_PROPERTY(QString messages READ messages WRITE setMessages NOTIFY messagesChanged)
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
Q_PROPERTY(bool isConnected READ isConnectedToHost NOTIFY isConnectedChanged)

QString m_userName;
QString m_messages;
QString m_message;
bool connectedToHost = false;
QTcpSocket* socket;

QString userName();
QString messages();
QString message();
void setUserName(const QString &userName);
void setMessages(const QString &messages);
void setMessage(const QString &message);
void printMessage(QString message);

public:
explicit ClientConnection(QObject *parent = nullptr);

public slots:
void on_connectButton_clicked();
void on_sendButton_clicked();

void socketConnected();
void socketDisconnected();
void socketReadyRead();
bool isConnectedToHost();

signals:

void userNameChanged();
void messagesChanged();
void isConnectedChanged();
void messageChanged();

};

#endif // CLIENTCONNECTION_H

This is the source code of TCP client implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include "clientconnection.h"

ClientConnection::ClientConnection(QObject *parent)
: QObject{parent}
{}

QString ClientConnection::userName()
{
return m_userName;
}

QString ClientConnection::messages()
{
return m_messages;
}

QString ClientConnection::message()
{
return m_message;
}

void ClientConnection::setUserName(const QString &userName)
{
if (userName == m_userName)
return;

m_userName = userName;
emit userNameChanged();
}

void ClientConnection::setMessages(const QString &messages)
{
m_messages = messages;
emit messagesChanged();
}

void ClientConnection::setMessage(const QString &message)
{
m_message = message;
emit messageChanged();
}

void ClientConnection::on_connectButton_clicked()
{
if (!connectedToHost)
{
socket = new QTcpSocket();

connect(socket, &QTcpSocket::connected, this, &ClientConnection::socketConnected);
connect(socket, &QTcpSocket::disconnected, this, &ClientConnection::socketDisconnected);
connect(socket, &QTcpSocket::readyRead, this, &ClientConnection::socketReadyRead);

socket->connectToHost("127.0.0.1", 8001);
}
else
{
socket->write("<font color=\"Orange\">" + m_userName.toUtf8() + " has left the chat room.</font><br>");

socket->disconnectFromHost();

delete socket;
}
}

void ClientConnection::on_sendButton_clicked()
{
socket->write("<br><font color=\"Blue\">" + m_userName.toUtf8() + "</font>: " + m_message.toUtf8());
}

void ClientConnection::socketConnected()
{
qDebug() << "Connected to server.";

printMessage("<font color=\"Green\">Connected to server.</font>");

socket->write("<br><font color=\"Purple\">" + m_userName.toUtf8() + " has joined the chat room.</font>");

connectedToHost = true;

emit isConnectedChanged();
}

void ClientConnection::socketDisconnected()
{
qDebug() << "Disconnected from server.";

printMessage("<br><font color=\"Red\">Disconnected from server.</font><br>");

connectedToHost = false;

emit isConnectedChanged();
}

void ClientConnection::socketReadyRead()
{
printMessage(socket->readAll());
}

void ClientConnection::printMessage(QString message)
{
m_messages.append(message);
emit messagesChanged();
}

bool ClientConnection::isConnectedToHost()
{
return connectedToHost;
}

This is the QML file to build the UI of the TCP client immplementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts
import QtQuick.Controls.Universal
import QML_Client 1.0

Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Client Window")
color: "mediumseagreen"

ClientConnection {
id: clientConnection
}

ColumnLayout {
id: infoColumn

anchors {
left: parent.left
right: parent.right
top: parent.top
bottom: parent.bottom
margins: 16
}

RowLayout {

id: userRow
height: 40
width: infoColumn.width

Text {
id: nameLabel
text: qsTr("Name:")
font.pointSize: 15
color: "white"
font.weight: Font.Medium
}

TextField {
id: textFieldName
Layout.fillWidth: true
onTextChanged: clientConnection.userName = text
placeholderText: qsTr("Input your name")
readOnly: clientConnection.isConnected

background: Rectangle {
implicitHeight: 30
anchors.fill: parent
height: 120
color: "white"
border.color: textFieldName.activeFocus ? "greenyellow" : "lightslategrey"
border.width: 3
radius: 5
}
}

Button {
id: connectButton
text: clientConnection.isConnected ? "Disconnect" : "Connect"
Layout.alignment: Qt.AlignRight
palette.buttonText: "white"
enabled: {
if(textFieldName.text === "") {
false
} else {
true
}
}

onClicked: {
clientConnection.on_connectButton_clicked()
forceActiveFocus()
}

contentItem: Text {
text: connectButton.text
font.weight: Font.Medium
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
}

ScrollView {
Layout.fillWidth: true
Layout.fillHeight: true
ScrollBar.vertical.policy: ScrollBar.AlwaysOff

TextArea {
id: textArea
wrapMode: TextArea.Wrap
text: clientConnection.messages
textFormat: Text.RichText
readOnly: true
selectionColor: "green"

background: Rectangle {
color: "white"
border.color: "springgreen"
border.width: 3
radius: 5
}
}
}

RowLayout {
TextField {
id: messageTextField
Layout.fillWidth: true
//text: clientConnection.message
placeholderText: qsTr("Input your message")
onTextChanged: clientConnection.message = text;

background: Rectangle {
implicitHeight: 30
color: "white"
border.color: messageTextField.activeFocus ? "greenyellow" : "lightslategrey"
border.width: 3
radius: 5
}
}

Button {
id: sendButton
text: "Send"
Layout.alignment: Qt.AlignRight

contentItem: Text {
text: sendButton.text
font.weight: Font.Medium
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}

onClicked: {
clientConnection.on_sendButton_clicked()
messageTextField.clear();
forceActiveFocus()
}

enabled: {
if(messageTextField.text === "" || textFieldName.text === "" || !clientConnection.isConnected) {
false
} else {
true
}
}
}
}
}
}