Chapter 3. Working with Qt

To know truly is to know by causes.
— Francis Bacon

Working with Qt

Before diving into the Qt library itself and moving on to the following chapters, you should familiarize yourself with the tools and utilities required for working with Qt.

Integrated Development Environment

There are many integrated development environments (IDEs) that allow you to create Qt projects comfortably and efficiently. Among the most well-known are Microsoft Visual Studio, Xcode, IBM Eclipse, as well as modern AI-assisted environments such as Cursor AI, Visual Studio Code with AI plugins, and even Xcode with AI tool integrations. Many of these environments provide CMake support, syntax highlighting for Qt-specific macros, and quick navigation to signal and slot definitions.

Qt Creator integrated development environment showing the project tree, code editor, and build output panels
Fig. 3.1. The Qt Creator integrated development environment window

However, one of the most convenient environments for Qt development remains Qt Creator (Fig. 3.1). This IDE is included in the official Qt distribution and is specifically designed for building cross-platform GUI and console applications using the full power of the framework. Qt Creator deserves its own dedicated chapter in this book — if you want to explore it in detail right away, refer to Chapter 47.

In modern development practice, it is increasingly common to use multiple IDEs simultaneously on the same project. This approach lets you take full advantage of each environment: for example, you can manage your project in Qt Creator, use Visual Studio Code or Cursor AI for quick code navigation and AI-assisted editing, and rely on Visual Studio or Xcode for building and debugging. Project files can be open in multiple environments concurrently without conflicts, which is especially convenient for large, multi-platform teams. This multi-IDE approach significantly increases flexibility and speeds up the development process.

Qt Assistant

Documentation is what developers use most often, and there is a tool that provides fast access to the information they need. That tool is Qt Assistant (Fig. 3.2), which works similarly to a web browser. Qt Assistant provides full-text search across all available Qt documentation. To set the path to a specific documentation location, you can use the -docPath parameter.

Qt Assistant documentation browser with full-text search across Qt class reference pages
Fig. 3.2. The Qt Assistant window

Qt Assistant is also integrated directly into Qt Creator. This allows you to use it without leaving the IDE and get context-sensitive help right inside the code editor. Developers can also embed Qt Assistant into their own applications using the QHelpEngine class.

Working with Build Systems

No programmer wants to manually specify linker options and library paths every time they compile their application. It is far more convenient to create a build configuration file that handles all the compiler and linker setup automatically.

Writing such files from scratch requires experience and a solid understanding of the application linking process, and their format varies depending on the target platform. This was once an essential skill for every programmer, but times have changed — not because configuration files have gotten simpler (if anything, they have become more complex), but because dedicated generator utilities now handle this work for you.

Working with CMake

In Qt 6, the primary build system is CMake (Cross-platform Make) — a cross-platform build automation system used to manage the software compilation process through easy-to-use configuration files.

To create a CMake-based project, you need to create a CMakeLists.txt file containing the necessary build instructions. A basic CMakeLists.txt for a Qt 6 project is shown in Listing 3.1.

Listing 3.1. Basic CMake build file for a Qt project (CMakeLists.txt)

cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)

set(PROJECT_SOURCES
    main.cpp
    mainwindow.cpp
    mainwindow.h
)

qt_add_executable(MyProject
    ${PROJECT_SOURCES}
)

target_link_libraries(MyProject PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Widgets
)

To generate the build files from CMake, run the following commands:

mkdir build
cd build
cmake ..

After that, you can compile the project with:

cmake --build

Or invoke your platform’s native build system directly (for example, make on Linux/macOS or msbuild on Windows).

Working with qmake

Although CMake is the preferred build system in Qt 6, the qmake utility is still supported for backward compatibility. It has been included with Qt since version 3.0. Notably, qmake is just as portable as Qt itself. When generating makefiles, qmake interprets project files with the .pro extension, which contain various configuration parameters. Remarkably, qmake is capable of generating not only makefiles, but also the .pro files themselves.

For example, if you have C++ source files in a directory, you can run the following command:

qmake –project

This will attempt to automatically generate a .pro file. This is convenient because you will not need to learn all the intricacies of .pro file creation right away. It is also useful when you have a large number of files and want to avoid entering their names manually. Converting a .pro file into a makefile is straightforward — simply run:

qmake file.pro –o Makefile
Generating Files with qmake on macOS

On macOS, there are two main options for generating files with qmake:

the first is generating a makefile for GNU C++:
qmake -spec macx-clang -o Makefile
the second is generating project files for Xcode:
qmake -spec macx-clang -o Makefile

Here, file.pro is the name of the project file, and Makefile is the name of the platform-specific makefile to be generated.

The first option is the default.

If you run qmake without any parameters, it will attempt to locate a .pro file in the current directory and, if found, automatically generate a makefile. This means that starting with only C++ source files, you can build an executable by running just three commands:

qmake –project
qmake
make

Of course, for more serious work you will need to edit .pro files directly to fine-tune your project configuration. Table 3.1 lists some of the project variables available in a .pro file; the full list can be found in the official Qt documentation included with the library (you can access it by launching Qt Assistant).

Table 3.1. Some project file variables

Variable Purpose
HEADERS List of header files included in the project
SOURCES List of implementation files (with the .cpp extension)
FORMS List of files with the .ui extension. These files are created by Qt Designer and contain the user interface description in XML format (see Chapters 40 and 44)
TARGET The name of the application. If this field is left empty, the application name will match the project file name
LIBS Specifies the list of libraries to link against when building the executable
CONFIG Specifies variables to be used by the compiler
DESTDIR Specifies the output path for the compiled executable
DEFINES Allows you to pass preprocessor definitions to the compiler. For example, this can be used to embed debug information for the debugger into the executable
INCLUDEPATH Path to the directory containing header files. This variable is useful when you have existing header files that you want to include in the current project
DEPENDPATH Specifies the dependencies required for compilation
SUBDIRS Specifies the names of subdirectories that contain .pro files
TEMPLATE Specifies the type of project. For example: app — application, lib — library, subdirs — subdirectories
TRANSLATIONS Specifies the translation files used in the project (see Chapter 31)
QT List of Qt modules used. For example: core, gui, widgets, network

Now let’s take a closer look at the anatomy of project files. A .pro file typically looks like this:

QT += core gui widgets
CONFIG += c++17
TEMPLATE = app
HEADERS += file1.h \
           file2.h
SOURCES += main.cpp  \
           file1.cpp \
           file2.cpp
TARGET = file

The first line lists the Qt modules used in the project. Starting with Qt 6, you must explicitly specify the required modules when building a GUI application. The second line defines the C++ standard version to use. The third line specifies the project type — in this case, an application, so TEMPLATE = app (if you needed to build a library, you would set TEMPLATE = lib instead). The HEADERS variable lists all header files belonging to the project, while SOURCES lists all implementation files. The TARGET line defines the application name.

As this example shows, qmake requires very little information because it relies on a local configuration file defined by the system configuration. This file is also important because the same qmake invocation will produce different makefiles depending on which platform it is run on. This is one of the key steps toward making project files themselves platform-independent.

A project file can be used to compile projects spread across multiple directories. A good example is the Examples.pro file located in the examples directory, available in the source archive (see Appendix 4). That file looks something like this:

TEMPLATE = subdirs
SUBDIRS = Example1 Example2 .... ExampleN
Removing Object Files

To remove the project’s object files, use the clean option. To remove object files, compiled executables, and generated makefiles, use the distclean option. For example: make distclean.

How to Migrate from qmake to CMake in Qt 6?

Migrating from qmake to CMake has become easier thanks to CMake being adopted as the primary build system in Qt 6. Here is a step-by-step migration process:

  1. Create a new sample CMake project in Qt Creator. This will help you understand the basic structure of a CMakeLists.txt file as recommended for Qt 6.
  2. Study the structure of the CMakeLists.txt file generated by Qt Creator for a new project, and compare it to your existing .pro file.
  3. Use the mapping table (Table 3.2) to convert qmake variables to their CMake equivalents.

Table 3.2. qmake to CMake variable mapping

qmake CMake
TEMPLATE = app qt_add_executable()
TEMPLATE = lib qt_add_library()
TARGET First argument to qt_add_executable() or qt_add_library()
HEADERS, SOURCES Passed as arguments to qt_add_executable() or qt_add_library()
QT += core gui find_package(Qt6 COMPONENTS Core Gui ...) + target_link_libraries()
DEFINES target_compile_definitions()
INCLUDEPATH target_include_directories()

To speed up the migration, you can use automated migration tools such as qmake2cmake, which can be installed via Python with the following command:

pip install qmake2cmake

Once installed, you can migrate an entire project tree with a single command:

qmake2cmake_all <MyProjects>/qt_project;

You can also use modern AI assistants such as ChatGPT (chat.com) or Claude (claude.ai) to automate routine migration tasks. For example, you can ask: Convert the following .pro file to an equivalent CMakeLists.txt for Qt 6 (then paste the contents of your .pro file).

A mapping example between qmake (Qt 5) and CMake (Qt 6) is shown in Table 3.3.

Table 3.3. qmake (Qt 5) to CMake (Qt 6) mapping

qmake CMake
QT += widgets find_package(Qt6 REQUIRED COMPONENTS Widgets) + target_link_libraries(app PRIVATE Qt6::Widgets)
SOURCES += main.cpp Passed as argument to add_executable(app main.cpp)
HEADERS += header.h Not usually required explicitly when CMAKE_AUTOMOC is enabled
CONFIG += c++17 set(CMAKE_CXX_STANDARD 17)
RESOURCES += res.qrc qt_add_resources(app res.qrc)

Project Recommendations for Qt

When implementing classes, it is best practice to split them into two separate files. The class definition goes into a header file with the .h extension, while the class implementation goes into a file with the .cpp extension. It is important to remember that every header file containing a class definition should include the #ifndef preprocessor directive. Its purpose is to prevent conflicts when the same header file is included more than once across different source files:

#ifndef _MyClass_h_
#define _MyClass_h_
class MyClass {
...
};
#endif //_MyClass_h_

This construct can also be replaced with an equivalent using #pragma once, which makes the header file more compact:

#pragma once
class MyClass {
...
};

By convention, a header file is typically named after the class it contains. In header files, forward declarations are preferred over direct #include directives for pointer types — this speeds up compilation. At the top of the class definition, the Q_OBJECT macro is placed for MOC — this is required if your class uses signals and slots. In other cases, where you do not need meta-information, this macro can be omitted. However, keep in mind that without meta-information, the qobject_cast<T>(obj) type cast will not be available:

class MyClass : public QObject {
Q_OBJECT
public:
    MyClass();
...
};

The main entry point of the application should be implemented in a separate file, which serves as the “launch pad” for the application. This file is conventionally named main.cpp. This is convenient because a project may consist of hundreds of files, and following this convention makes it easy to locate the project’s entry point.

Following the recommendations outlined here can be very beneficial. Projects tend to grow over time, so it is good practice to establish a clear structure from the start — so that you and your teammates can navigate both your own projects and other Qt-based projects with ease.

The Meta-Object Compiler (MOC)

The Meta-Object Compiler (MOC, Meta Object Compiler) is not actually a compiler — it is a preprocessor that runs during the build process, generating additional C++ code based on class definitions. This is necessary because signal and slot declarations in source code alone are not sufficient for compilation. The signal/slot code must be transformed into code that the C++ compiler can understand. The generated code is saved in a file named moc_<filename>.cpp.

If you work with project files, you may not even need to think about MOC, since its invocation is handled automatically by the build system. To generate a moc file manually, use the following command:

moc –o proc.moc proc.h
Do not include moc files at the end of the main source file!

Generated moc files should not be included via a #include "main.moc" directive at the end of the main source file, like this:

#include <QtWidgets>
int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    ...
    return app.exec();
}
#include "main.moc"

It is better to compile them separately and let the linker attach them to the main program. That said, for demonstration programs this rule can be relaxed so that all code fits within a single main.cpp file.

After executing this command, MOC will generate the additional file proc.moc.

For every class derived from QObject, MOC provides an instance of a class derived from QMetaObject. This object contains structural information about the object — for example, signal/slot connections, the class name, and the inheritance hierarchy.

The Resource Compiler (RCC)

Almost every application accesses external resources at some point — bitmap images, translation files, and so on. Relying on external resource files is not always reliable or efficient, since those files can be deleted or become unavailable for various reasons, which may affect the application’s correct behavior, appearance, and functionality. The resource compiler makes it possible to embed such files directly into the executable, ensuring that the application always has access to the required resources at runtime. Special naming conventions allow these embedded resources to be referenced unambiguously. All required files (resources) must be described along with their paths in a special file with the .qrc extension (Qt Resource Collection). This description is written in XML notation. For example:

<!DOCTYPE RCC><RCC version="1.0">
<qresource>
    <file>images/open.png</file>
    <file>images/quit.png</file>
</qresource>
</RCC>

This file will be processed by the resource compiler (rcc) to generate a single C++ source file containing all the data from open.png and quit.png. This generated file is then compiled and linked together with the rest of the project files. All resource data is stored inside the C++ file as a single large array.

This approach ensures that the required resources are always available, eliminating problems caused by missing files during installation. The .qrc file itself must be referenced in the .pro file using the RESOURCES variable, so that qmake picks up the resource information. For example:

RESOURCES = images.qrc

When using CMake, resources are added using the qt_add_resources() function:

qt_add_resources(MyProject "resources"
    PREFIX "/"
    FILES
        images/open.png
        images/quit.png
)

To use open.png — or rather, the bitmap image it contains — you can do the following:

plbl->setPixmap(QPixmap(":/images/open.png"));

In this example, all bitmaps are conveniently placed inside the images directory, which is the ideal case. In practice, however, it is not always possible to organize resource files this way. The paths for accessing these files can sometimes be quite long, making it tedious to type them out repeatedly. This inconvenience can be solved by using aliases, which are defined in the XML file using the alias attribute of the <file> tag. For example:

<!DOCTYPE RCC><RCC version="1.0">
<qresource>
    <file alias="open.png">../../../very/long/path/images/open.png</file>
    <file alias="quit.png">../../../very/long/path/images/quit.png</file>
</qresource>
</RCC>

Now we can refer to our resource files from the application like this:

plbl->setPixmap(QPixmap(":/open.png"));

Without using aliases, the same call would look like this:

plbl->setPixmap(QPixmap(":/very/long/path/images/open.png"));

You will agree that the alias-based approach is much cleaner.

The QResource class for reading files from a resource

The QtCore module provides the QResource class for reading resource files directly, without any intermediate steps. To do this, create an object and pass the filename to the QResource constructor. You can then use the data() method to obtain a pointer to the raw data of the specified file.

Qt Project Structure

We have now examined the individual utilities used in a Qt project. Let’s bring them all together to understand how they interact. The structure of a Qt project is straightforward — in addition to C++ source files, it typically includes a build configuration file (CMakeLists.txt for CMake, or a .pro file for qmake). From this file, platform-specific build files are generated. These files contain all the instructions needed to produce the final executable (Fig. 3.3).

Diagram of the Qt build process from CMakeLists.txt through MOC, RCC, compiler, and linker to the final executable
Fig. 3.3. Diagram of the executable build process

The build configuration includes an invocation of MOC to generate additional C++ code and the required header files. If the project contains a .qrc file, a C++ file containing the resource data will also be generated. All source files are then compiled by the C++ compiler into object files, which are linked together by the linker to produce the final executable.

Debugging Methods

There are no programs without bugs and defects. Finding and fixing bugs often takes a significant portion of a developer’s time. Some of the tools that help reduce their number include:

  1. code reviews, in which source code is examined by other developers;
  2. writing automated test classes, as described in detail in Chapter 45.

Bugs can be minimized, but never fully eliminated. When a tricky bug sneaks into your application, the first tool you will reach for is the debugger. The role of the debugger is to provide a runtime environment in which you can track data changes as the program executes, helping you understand why your application is not behaving as intended. Thanks to Qt’s cross-platform nature, developers can use any debugger they prefer — for example, GDB or the debugger built into Microsoft Visual Studio.

Moreover, Qt Creator supports out-of-the-box integration with the most popular debuggers (GDB, LLDB, CDB, and others). You can launch, pause, and step through code, inspect variable values, and examine the call stack directly from the Qt Creator interface — making the debugging process intuitive and efficient, regardless of the debugger you choose. We will cover this in more detail in Chapter 47.

Other Debugging Methods

One of the standard debugging techniques is inserting output statements into the source code, which lets you inspect variable values and compare them against expected results. This approach is widely used by developers because it is effortless to add these statements or wrap them in a dedicated dump method. In Qt, a good example of this approach is the QObject::dumpObjectInfo() method, which prints the object’s meta-information to the output.

Qt provides macros and functions for debugging that allow you to embed various checks and diagnostic output directly into your application.

The QtGlobal header file defines two macros: Q_ASSERT() and Q_CHECK_PTR():

  1. Q_ASSERT() takes a boolean expression as its argument and prints a warning message if the expression evaluates to false;
  2. Q_CHECK_PTR() takes a pointer and prints a warning message if the pointer is 0, which indicates that either the pointer was not initialized or a memory allocation failed.

Qt provides the global functions qDebug(), qWarning(), and qFatal(), which are also defined in the QtGlobal header. They work similarly to printf() — you pass a format string along with various parameters. In Microsoft Visual Studio, output from these functions appears in the debugger output window; on Linux, it goes to the standard error stream.

A note on qFatal()

Calling qFatal() immediately terminates the entire application after printing the message.

If you need to redirect message output, you can create and install a custom message handler function using qInstallMessageHandler(). This function takes as its argument a pointer to a message handler function with the following signature:

void fct(QtMsgType type, const QMessageLogContext& context, const QString& msg)

Replace fct with the actual function name. The first argument represents the message type, which is one of the QtMsgType enumeration values: QtDebugMsg, QtWarningMsg, QtInfoMsg, QtCriticalMsg, or QtFatalMsg. The second argument provides additional context about the message, and the third is the message itself. The following code fragment illustrates this (Listing 3.2).

Listing 3.2. Redirecting message output to a file

void messageToFile(QtMsgType                 type,
                   const QMessageLogContext& context,
                   const QString&            msg
                  )
{
    QFile file("protocol.log");
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
        return;

    QString strDateTime =
        QDateTime::currentDateTime().toString("dd.MM.yy-hh:mm");

    QTextStream out(&file);
    switch (type) {
     case QtDebugMsg:
         out << strDateTime << "Debug: " << msg
             << ", " << context.file << endl;
         break;
     case QtWarningMsg:
         out << strDateTime << "Warning: " << msg
             << ", " << context.file << endl;
         break;
     case QtCriticalMsg:
         out << strDateTime << "Critical: " << msg
             << ", " << context.file << endl;
         break;
     case QtFatalMsg:
         out << strDateTime << "Fatal: " << msg
             << ", " << context.file << endl;
         abort();
    case QtInfoMsg:
        out << strDateTime << "Info: " << msg
            << ", " << context.file << endl;
        break;
    }
}

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    qInstallMessageHandler(messageToFile);
...
}

Now all qDebug(), qWarning(), and qFatal() messages will be written to the file protocol.log instead of being printed to the console. This is extremely useful for investigating errors and unexpected behavior that occur on the tester’s or end user’s machine. You can always ask them to send you the protocol.log file for further analysis.

Reverting to console output

If you need to restore console output in your application, simply call the global function qInstallMessageHandler() and pass 0 as its argument.

To simplify debugging, it is recommended to assign names to all objects. You can then find any object at any time by calling QObject::objectName(). This also enables you to use QObject::dumpObjectInfo() at runtime, which prints the object’s internal information.

During debugging, you can also install an event filter on the QCoreApplication object — in this case, the filter will be the first object to receive and process events from all objects in the application (see Chapter 15).

The simplest way to produce output in Qt is to use the QDebug class object. This object closely resembles the standard C++ output stream object cout. For example, you can print a message to the debugger or console using qDebug() as follows:

qDebug() << "Test";

This function creates a QDebug stream object, passing the previously mentioned QtDebugMsg argument to its constructor. You could also write it like this:

QDebug(QtDebugMsg) << "Test";

However, as you can see, the previous form is more concise, so that is the recommended style.

Qt 6 also added qInfo() for printing informational messages, and qCInfo() for printing informational messages with a category:

qInfo() << "This is an informational message";
qCInfo("network") << "Network operation completed successfully";

It is important to understand that output from qDebug() is produced in both debug and release builds. If you want to suppress all output in a release build and show messages only in a debug build, you can implement an empty output function dummyOutput() and install it via qInstallMessageHandler(), as shown in Listing 3.3.

Listing 3.3. Suppressing output from qDebug(), qWarning(), and qFatal()

void dummyOutput(QtMsgType, const QMessageLogContext&, const QString&)
{
}

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
#ifndef QT_DEBUG
    qInstallMessageHandler(dummyOutput);
#endif
}

You should use the following form with qDebug():

qDebug() << "Test1" << 123 << "Test2" << 456;

Now, when you build a release version of your application, you can be confident that all debug output from qDebug(), qWarning(), and qFatal() will be hidden from end users.

Debug output also supports manipulators — for example, hex and uppercasedigits. The former causes all integer values to be printed in hexadecimal form, while the latter converts all lowercase letters to uppercase. As an example, let’s implement a small program that prints the value of variable n in hexadecimal with uppercase letters:

int n = 7777;
qDebug() << "DEC:" << n << "= HEX:" << hex << uppercasedigits << n;

Here is what we will see on screen:

DEC: 7777 =HEX: 1E61

After each output section, qDebug() inserts a space. For example:

qDebug() << 1 << 2 << 3 << 4;

Output:

1 2 3 4

You can suppress this behavior using the nospace() method of the QDebug class:

qDebug().nospace() << 1 << 2 << 3 << 4;

Now the output will look like this:

1234

Qt Global Definitions

Qt’s QtGlobal header file contains several macros and functions that can be very useful when writing applications.

The template functions qMax(a, b) and qMin(a, b) are used to find the maximum and minimum of two values:

int n = qMax<int>(3, 5); // n = 5
int n = qMin<int>(3, 5); // n = 3

The function qAbs(a) returns the absolute value:

int n = qAbs(-5); // n = 5

The function qRound() rounds a number to the nearest integer:

int n = qRound(5.2);  // n = 5
int n = qRound(-5.2); // n = -5

The function qBound() returns a value clamped between a minimum and a maximum:

int n = qBound(2, 12, 7); // n = 7

Here is another interesting function. Comparing two floating-point values for exact equality is one of the most common programming mistakes. The function qFuzzyCompare() takes responsibility for handling this correctly. It accepts two values of type double or float and returns true if the values are considered equal, or false otherwise. The comparison is performed in a relative manner, where the precision increases as the magnitude of the compared values decreases. The only value that poses a challenge for this function is zero. However, there is a solution: simply ensure that the values being compared are either equal to or greater than 1.0. For example:

double dValue1 = 0.0;
double dValue2 = myFunction();

if (qFuzzyCompare(1 + dValue1, 1 + dValue2)) {
    // Values are equal
}
The qFuzzyCompare() function and unit tests

This function can also be very useful when writing unit tests, as described in Chapter 45.

Table 3.4. Qt integer types

Qt Type C++ Equivalent Size
qint8 signed char 8 bits
quint8 unsigned char 8 bits
qint16 short 16 bits
quint16 unsigned short 16 bits
qint32 int 32 bits
quint32 unsigned int 32 bits
qint64 __int64 or long long 64 bits
quint64 unsigned __int64 or unsigned long long 64 bits
qlonglong Same as qint64 64 bits
qulonglong Same as quint64 64 bits

As shown in Table 3.4, the most nuanced types are qint64 and quint64. Let’s verify the bit widths listed for them in the table, along with their minimum and maximum values:

qDebug() << "Number of bits for qint64 ="  << (sizeof(qint64) * 8);
qDebug() << "Minimum of qint64 = -"        << ~(~quint64(0) >> 1);
qDebug() << "Maximum of qint64 ="          << (~quint64(0) >> 1);
qDebug() << "Number of bits for quint64 =" << (sizeof(quint64) * 8);
qDebug() << "Minimum of quint64 ="         << 0;
qDebug() << "Maximum of quint64 ="         << ~quint64(0);

Output:

Number of bits for qint64 = 64
Minimum of qint64 = -9223372036854775808
Maximum of qint64 = 9223372036854775807
Number of bits for quint64 = 64
Minimum of quint64 = 0
Maximum of quint64 = 18446744073709551615

Qt Library Information

It is sometimes very useful to retrieve information about the Qt library currently installed on your machine — for example, to find out which directory Qt uses to store its plugin files, or to verify the current Qt version. The QLibraryInfo class is responsible for providing this kind of information and exposes a number of static methods for this purpose. The following small example demonstrates their usage (Listing 3.4).

Listing 3.4. Using the static methods of QLibraryInfo

#include <QDebug>
#include <QLibraryInfo>

int main()
{
    qDebug() << "Is Debug Build:"
             << QLibraryInfo::isDebugBuild();

    qDebug() << "Locations";
    qDebug() << "  Headers:"
             << QLibraryInfo::path(QLibraryInfo::HeadersPath);
    qDebug() << "  Libraries:"
             << QLibraryInfo::path(QLibraryInfo::LibrariesPath);
    qDebug() << "  Binaries:"
             << QLibraryInfo::path(QLibraryInfo::BinariesPath);
    qDebug() << "  Prefix"
             << QLibraryInfo::path(QLibraryInfo::PrefixPath);
    qDebug() << "  Documentation: "
             << QLibraryInfo::path(QLibraryInfo::DocumentationPath);
    qDebug() << "  Plugins:"
             << QLibraryInfo::path(QLibraryInfo::PluginsPath);
    qDebug() << "  Data:"
             << QLibraryInfo::path(QLibraryInfo::DataPath);
    qDebug() << "  Settings:"
             << QLibraryInfo::path(QLibraryInfo::SettingsPath);

    qDebug() << "  Examples:"
             << QLibraryInfo::path(QLibraryInfo::ExamplesPath);
    qDebug() << "Version:" << QLibraryInfo::version().toString();
}

Here is the output of this program for Qt 6.9.0 installed on a Windows machine:

Is Debug Build: false
Locations
  Headers: "C:\Qt\6.9.0\mingw_64\include"
  Libraries: "C:\Qt\6.9.0\mingw_64\lib"
  Binaries: "C:\Qt\6.9.0\mingw_64\bin"
  Prefix "C:\Qt\6.9.0\mingw_64"
  Documentation:  "C:\Qt\6.9.0\mingw_64\doc"
  Plugins: "C:\Qt\6.9.0\mingw_64\plugins"
  Data: "C:\Qt\6.9.0\mingw_64"
  Settings: " "
  Examples: "C:\Qt\6.9.0\mingw_64\examples"
  Version: “6.9.0"

Summary

In this chapter, we explored the structure of a typical Qt project and the processes that take place “behind the scenes” when building a finished executable. We reviewed the two build systems used in Qt 6: CMake and qmake. Switching to CMake not only provides access to modern build tooling, but also significantly simplifies cross-platform development and integration of Qt with third-party libraries. Automated conversion tools and AI assistants such as ChatGPT or Claude can save time, minimize errors, and make the migration as smooth and efficient as possible.

We took a detailed look at the MOC preprocessor, which generates additional signal/slot support code at build time, and explored Qt’s global definitions. We also reviewed Qt’s debugging capabilities and the special macros designed for that purpose.

Having worked through the material in this chapter, you are now ready to build and debug your own Qt 6 applications — using either the traditional qmake-based workflow or the recommended CMake build system.


Unlock the full potential of this chapter! Access the supplementary materials prepared for it, ask a question, share your experience, or join the discussion at: https://qt-book.com/en/03-69-en/


Leave a Reply

Your email address will not be published. Required fields are marked *