Why Learn Qt C++
Qt is a mature, open-source C++ UI framework with commercial support. It is especially well suited for industrial, medical, and other device-oriented software.
- Stability and maturity: Qt has a long production track record and a large ecosystem
- Cross-platform advantage: one source tree can build applications for Windows, macOS, and Linux
- Command-line support: Qt is not limited to GUI apps; it also works well for console tools
- Learning value: it has rich documentation, clear conventions, and practical examples that help deepen C++ skills
Development Environment
Installing Qt Libraries and Tools
- Use the Qt Online Installer
- On Linux, package managers such as
aptcan install the distribution-provided build - You can also build from source. The example below uses Linux:
# Install dependencies
sudo apt install -y build-essential libgl1-mesa-dev \
libfontconfig1-dev \
libfreetype6-dev \
libx11-dev \
libx11-xcb-dev \
libxext-dev \
libxfixes-dev \
libxi-dev \
libxrender-dev \
libxcb1-dev \
libxcb-cursor-dev \
libxcb-glx0-dev \
libxcb-keysyms1-dev \
libxcb-image0-dev \
libxcb-shm0-dev \
libxcb-icccm4-dev \
libxcb-sync-dev \
libxcb-xfixes0-dev \
libxcb-shape0-dev \
libxcb-randr0-dev \
libxcb-render-util0-dev \
libxcb-util-dev \
libxcb-xinerama0-dev \
libxcb-xkb-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libssl-dev libclang-dev libbluetooth-dev
# Configure
./configure -release -prefix /opt/qt/6.5 \
-no-accessibility -openssl-linked \
-disable-deprecated-up-to 0x060500 \
-skip qt3d \
-skip qt5compat \
-skip qtactiveqt \
-skip qtdatavis3d \
-skip qtdeclarative \
-skip qtdoc \
-skip qtlocation \
-skip qtlottie \
-skip qtmqtt \
-skip qtopcua \
-skip qtscxml \
-skip qtquick3d \
-skip qtquick3dphysics \
-skip qtquickeffectmaker \
-skip qtquicktimeline \
-skip qtwebengine \
-skip qtwebview \
-skip qtvirtualkeyboard \
-- -DOPENSSL_USE_STATIC_LIBS=ON
# Build and install
cmake --build . --parallel && sudo cmake --install .
cmake --build . --target docs && sudo cmake --build . --target install_docs
# Add Qt to PATH
echo 'export PATH="/opt/qt/6.5/bin:$PATH"' >> ~/.profile
Code Editing and Compilation
- Use Qt Creator or a common editor such as VSCode
- A minimal CMake setup looks like this:
cmake_minimum_required(VERSION 3.14)
project(test LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Core)
link_libraries(Qt6::Core)
add_executable(test main.cpp)
main.cpp:
#include <QCoreApplication>
#include <QDate>
#include <QTimer>
int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
a.setApplicationVersion(QDate::currentDate().toString("yyyyMMdd"));
a.setOrganizationName("iotworker");
qInfo() << "App:" << a.applicationName() << "Version:" << a.applicationVersion() << "Pid:" << a.applicationPid();
qInfo() << "Organization:" << a.organizationName();
a.connect(&a, &QCoreApplication::aboutToQuit, []() {
qDebug() << "about to quit";
});
QTimer::singleShot(3000, &a, QCoreApplication::quit);
return a.exec();
}
Core Concepts
Signals and Slots
Signals and slots are one of Qt Core’s basic features for object-to-object communication. They work like callbacks, but they are usually easier to read and connect.
// test.h
#include <QDebug>
#include <QObject>
class Counter : public QObject {
Q_OBJECT
public slots:
void setValue(int value)
{
mValue = value;
qDebug() << objectName() << __func__ << value;
emit valueChanged(value);
}
signals:
void valueChanged(int value);
private:
int mValue = 0;
};
// main.cpp
Counter a, b;
a.setObjectName("A");
b.setObjectName("B");
QObject::connect(&a, &Counter::valueChanged, &b, &Counter::setValue);
a.setValue(1);
- Signal methods are
public, but they are usually intended for the class itself and its subclasses - A signal has no return value and usually does not take parameters
- Signals are preprocessed by
mocat build time - After
emit, connected slots are called in connection order, except for queued connections - A slot is an ordinary C++ function and can be called directly
QObject
QObject is an important base class in Qt. It provides signals and slots, the property system, event handling, and object lifetime management.
Important Macros
Q_OBJECT: the core macro for the meta-object system; subclasses of Qt types usually need itQ_CLASSINFO: attaches custom key-value metadata to a classQ_ENUM: exposes enum values to the Qt meta-object system and string conversion
Important Members
// Connect signals and slots
QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection);
// Meta object access
const QMetaObject QObject::staticMetaObject;
// Signals
void destroyed(QObject *obj = nullptr);
void objectNameChanged(const QString &objectName);
// Slots
void deleteLater();
// Common functions
bool blockSignals(bool block);
const QObjectList &children() const;
void setParent(QObject *parent);
QObject *parent() const;
void dumpObjectInfo();
void dumpObjectTree();
void setObjectName(const QString &name);
QString objectName() const;
bool setProperty(const char *name, const QVariant &value);
QVariant property(const char *name) const;
QCoreApplication
QCoreApplication is the core class for non-GUI applications and the base class of QApplication.
Application Configuration
QCoreApplication a(argc, argv);
a.setApplicationName("test");
a.setApplicationVersion("0.1");
a.setOrganizationName("iotworker");
a.setOrganizationDomain("test.com");
qDebug() << a.applicationDirPath();
qDebug() << a.applicationName();
qDebug() << a.applicationVersion();
Argument Parsing
QCommandLineParser parser;
parser.setApplicationDescription("test");
parser.addHelpOption();
parser.addVersionOption();
parser.addOptions({
{ "s", "short option", "valueName", "defaultValue" },
{ "long", "long option" }
});
parser.addPositionalArgument("pos1", "position args");
parser.process(a);
if (parser.isSet("s")) {
qDebug() << parser.value("s");
}
Logging System
Q_LOGGING_CATEGORY(test, "Test")
static void myLogOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
// Custom log format
}
int main(int argc, char* argv[])
{
qInstallMessageHandler(myLogOutput);
QLoggingCategory::setFilterRules("*.debug=false\nTest.critical=false");
qCDebug(test, "debug message");
qCCritical(test, "error message");
}
Timers
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, []() {
qDebug() << "interval timeout";
});
QTimer::singleShot(3000, [&timer]() {
qDebug() << "single shot timeout";
timer.start(1000);
});
Common Modules
QDateTime
QDateTime is used for date and time handling.
// Current time
auto now = QDateTime::currentDateTime();
qDebug() << now;
// Timestamp conversion
qDebug() << QDateTime::fromMSecsSinceEpoch(1687907736713, QTimeZone::UTC);
qDebug() << now.toMSecsSinceEpoch();
// String conversion
qDebug() << QDateTime::fromString("20230628", "yyyyMMdd");
qDebug() << now.toString("yyyy/MM/dd HH:mm:ss.zzz");
// Time arithmetic
qDebug() << now.addDays(1);
qDebug() << now.daysTo(now.addYears(1));
JSON and CBOR Handling
JSON
// Parse JSON
QJsonDocument js = QJsonDocument::fromJson(js_str);
QJsonObject root_obj = js.object();
// Modify JSON
root_obj["new"] = true;
root_obj.remove("isStudent");
root_obj["age"] = 30;
// Iterate
for (auto&& i : root_obj["languages"].toArray()) {
qDebug() << i;
}
// Serialize
qDebug() << QJsonDocument(root_obj).toJson().data();
CBOR
// Create CBOR
QCborMap root_obj = {
{ 1, true },
{ 2, 1 },
{ 3, 1.2 },
{ "4", "hi" }
};
// Serialize
qDebug() << root_obj.toCbor().toHex();
qDebug() << QJsonDocument(root_obj.toJsonObject()).toJson().data();
// Parse
QCborValue cbor = QCborValue::fromCbor(data);
root_obj = cbor.toMap();
File System Operations
QDir
QDir dir;
qDebug() << QDir::current() << QDir::homePath();
// Directory operations
qDebug() << dir.mkdir("./hello") << dir.rmdir("hello");
dir.setFilter(QDir::Files);
dir.setNameFilters(QStringList("*.txt"));
// File information
QFileInfoList list = dir.entryInfoList();
for (auto&& i : list) {
qDebug() << i.fileName() << i.size() << i.lastModified();
}
QFile
QFile f("./test.txt");
qDebug() << f.exists() << f.size();
// Write a file
if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&f);
out << "The magic number is: " << 49 << "\n";
f.close();
}
// Read a file
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&f);
while (!in.atEnd()) {
QString line = in.readLine();
qDebug() << line;
}
f.close();
}
QFileSystemWatcher
QFileSystemWatcher watcher(QStringList() << "/tmp" << "/tmp/test");
QObject::connect(&watcher, &QFileSystemWatcher::directoryChanged, [](const QString& path) {
qDebug() << "dir" << path;
});
QObject::connect(&watcher, &QFileSystemWatcher::fileChanged, [](const QString& path) {
qDebug() << "file" << path;
});
QSettings
QSettings is used to manage and save application settings.
QCoreApplication a(argc, argv);
a.setApplicationName("test");
a.setOrganizationName("iotworker");
a.setOrganizationDomain("test.com");
QSettings settings;
// Basic operations
settings.setValue("interval", 30);
qDebug() << settings.value("interval").toInt();
// Group operations
settings.beginGroup("group1");
settings.setValue("name", "Bob");
qDebug() << settings.value("name").toString();
settings.endGroup();
// Query and delete
qDebug() << settings.allKeys();
qDebug() << settings.contains("name");
settings.remove("group1/name");
Summary
Qt C++ provides a complete application development framework, from the basic object model to higher-level modules, covering most parts of modern application development. With this guide, you should now have a solid grasp of:
- Qt development environment setup
- The core object model and the signals-and-slots mechanism
- Application infrastructure
- Common modules and how to use them
Qt’s strength lies in its cross-platform capability and rich library set, which lets developers focus on business logic instead of platform differences. For C++ developers who need to build cross-platform applications, Qt is a framework worth learning.