A Boilerplate Application

    The example demonstrates the use of file access and the how to write text to a a file using text codecs using a text stream. For binary data, there is a cross-platform binary stream called QDataStream that takes care of endianess and other details. The different classes we use are included using their class name at the top of the file. You can also include classes using the module and class name e.g. #include <QtCore/QFile>. For the lazy, there is also the possibility to include all the clases from a module using #include <QtCore>. For instance, in QtCore you have the most common classes used for an application that are not UI related. Have a look at the QtCore class list (opens new window) or the .

    You build the application using CMake and make. CMake reads a project file, CMakeLists.txt and generates a Makefile which is used to build the application. CMake supports other build systems too, for example ninja. The project file is platform independent and CMake has some rules to apply the platform specific settings to the generated makefile. The project can also contain platform scopes for platform-specific rules, which are required in some specific cases.

    Here is an example of a simple project file generated by Qt Creator. Notice that Qt attempts to create a file that is compatible with both Qt 5 and Qt 6, as well as various platforms such as Android, OS X and such.

    1. cmake_minimum_required(VERSION 3.14)
    2. project(projectname VERSION 0.1 LANGUAGES CXX)
    3. set(CMAKE_INCLUDE_CURRENT_DIR ON)
    4. set(CMAKE_AUTOUIC ON)
    5. set(CMAKE_AUTOMOC ON)
    6. set(CMAKE_AUTORCC ON)
    7. set(CMAKE_CXX_STANDARD 11)
    8. set(CMAKE_CXX_STANDARD_REQUIRED ON)
    9. # QtCreator supports the following variables for Android, which are identical to qmake Android variables.
    10. # Check https://doc.qt.io/qt/deployment-android.html for more information.
    11. # They need to be set before the find_package(...) calls below.
    12. #if(ANDROID)
    13. # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
    14. # if (ANDROID_ABI STREQUAL "armeabi-v7a")
    15. # set(ANDROID_EXTRA_LIBS
    16. # ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
    17. # ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
    18. # endif()
    19. #endif()
    20. find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Quick REQUIRED)
    21. find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Quick REQUIRED)
    22. set(PROJECT_SOURCES
    23. main.cpp
    24. qml.qrc
    25. )
    26. qt_add_executable(projectname
    27. MANUAL_FINALIZATION
    28. ${PROJECT_SOURCES}
    29. )
    30. else()
    31. if(ANDROID)
    32. add_library(projectname SHARED
    33. ${PROJECT_SOURCES}
    34. )
    35. else()
    36. add_executable(projectname
    37. ${PROJECT_SOURCES}
    38. )
    39. endif()
    40. endif()
    41. target_compile_definitions(projectname
    42. PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
    43. target_link_libraries(projectname
    44. PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick)
    45. set_target_properties(projectname PROPERTIES
    46. MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
    47. MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    48. MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    49. )
    50. if(QT_VERSION_MAJOR EQUAL 6)
    51. qt_import_qml_plugins(projectname)
    52. qt_finalize_executable(projectname)
    53. endif()

    The simple code example above just writes the text and exits the application. For a command line tool, this is good enough. For a user interface you need an event loop which waits for user input and somehow schedules draw operations. Here follows the same example now uses a button to trigger the writing.

    Our main.cpp surprisingly got smaller. We moved code into an own class to be able to use Qt’s signal and slots for the user input, i.e. to handle the button click. The signal and slot mechanism normally needs an object instance as you will see shortly, but it can also be used with C++ lambdas.

    In the main function we create the application object, a window, and then start the event loop using exec(). For now, the application sits in the event loop and waits for user input.

    1. int main(int argc, char** argv)
    2. QApplication app(argc, argv); // init application
    3. return app.exec(); // execute event loop
    4. }

    The main window itself is a widget. It becomes a top-level window as it does not have any parent. This comes from how Qt sees a user interface as a tree of UI elements. In this case, the main window is the root element, thus becomes a window, while the push button, that is a child of the main window, becomes a widget inside the window.

    Additionally, we define a public slot called storeContent() in a custom section in the header file. Slots can be public, protected, or private, and can be called just like any other class method. You may also encounter a signals section with a set of signal signatures. These methods should not be called and must not be implemented. Both signals and slots are handled by the Qt meta information system and can be introspected and called dynamically at run-time.

    1. #include "mainwindow.h"
    2. MainWindow::MainWindow(QWidget *parent)
    3. : QMainWindow(parent)
    4. {
    5. m_button = new QPushButton("Store Content", this);
    6. setCentralWidget(m_button);
    7. connect(m_button, &QPushButton::clicked, this, &MainWindow::storeContent);
    8. }
    9. MainWindow::~MainWindow()
    10. {
    11. }
    12. void MainWindow::storeContent()
    13. {
    14. qDebug() << "... store content";
    15. QString message("Hello World!");
    16. QFile file(QDir::home().absoluteFilePath("out.txt"));
    17. if(!file.open(QIODevice::WriteOnly)) {
    18. qWarning() << "Can not open file with write access";
    19. return;
    20. }
    21. QTextStream stream(&file);
    22. stream << message;

    In the main window, we first create the push button and then register the signal clicked() with the slot storeContent() using the connect method. Every time the signal clicked is emitted the slot storeContent() is called. And now, the two objects communicate via signal and slots despite not being aware of each other. This is called loose coupling and is made possible using the QObject base class which most Qt classes derive from.