Logo
blank Skip to main content

How to Write a Minimal Qt QML application with CMake

C++

Qt is a popular cross-platform application framework developed by the Qt Company and distributed via both proprietary and open source licenses. Qt is designed for creating applications that can run on different platforms without the need to change codebase for each platform. Itโ€™s most often used for creating multi-platform applications and graphical user interfaces (GUIs).

This tutorial will show you how to create a simple Qt application with the Qt Quick graphical user interface. Weโ€™ll use CMake as our project format and C++ as the language for our source code. Our example application is simple in design and consists of just one window.

Qt Quick is a user interface library for creating declarative user interfaces that are separated from programming logic. This framework is implemented as part of the Qt library set. QML is a user interface specification and programming language.

CMake is the project generator for many platforms and build systems. The CMake build system uses the unified file format and is able to generate makefiles, Visual Studio solutions, and projects for many other build systems from the same project file. You can check out some CMake examples on the official website if you want to learn more.

Prerequisites

In order to develop an application using CMake with Qt QML, we first need to make sure that we have all the necessary tools.

Compiling and running our sample project requires GCC, CMake, GNU Make, and the Qt development libraries with Qt Quick enabled. This tutorial specifies how to install pre-compiled packages from standard repositories.

Environment Setup

Debian-based Systems

The following instructions apply to:

  • Ubuntu 16.04
  • Debian 9
ShellScript
sudo apt-get install -y \
    build-essential \
    cmake \
    qtbase5-dev \
    qtdeclarative5-dev \
    qtquickcontrols2-5-dev \
    qml-module-qtquick2 \
    qml-module-qtquick-controls

RedHat-based Systems

The following instructions apply to:

  • Fedora 22 and higher
ShellScript
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y \
    cmake \
    gcc-g++ \
    qt5-qtbase-devel \
    qt5-qtdeclarative-devel \
    qt5-qtquickcontrols \
    qt5-qtquickcontrols2-devel
  • CentOS 7
ShellScript
sudo yum groupinstall -y "Development Tools"
sudo yum install -y \
    cmake \
    qt5-qtbase-devel \
    qt5-qtdeclarative-devel \
    qt5-qtquickcontrols \
    qt5-qtquickcontrols2-devel

Ensure the success of your cross-platform application

Get yourself an expert dedicated team that will choose the most suitable tools, technologies, and development approaches for your project.

Directory Structure

The directory of the project is laid out as follows:

ShellScript
$ tree
.
โ”œโ”€โ”€ CMakeLists.txt
โ””โ”€โ”€ src
    โ”œโ”€โ”€ CMakeLists.txt
    โ”œโ”€โ”€ main.cpp
    โ”œโ”€โ”€ main.qml
    โ””โ”€โ”€ qml.qrc
1 directory, 5 files

All source files for the project go in src, and the main project specifications in CMake format go in CMakeLists.txt. Larger projects tend to be organized in sub-directories for modules, executables, and libraries, each with its individual sub-directory and CMakeLists.txt file.

Main Window GUI File

With this example, we start with the declarative file for the user interface, which is specified in src/main.qml. The code below creates a small window without any controls:

src/main.qml

Python
import QtQuick 2.0
import QtQuick.Controls 1.0
ApplicationWindow
{
    visible: true
    width: 640
    height: 480
    title: qsTr("Minimal Qml")
}

The next code imports all necessary modules. The exact version of QtQuick is not the same as the version of the Qt framework used. The QtQuick.Controls library contains some basic controls for the user interface.

Python
import QtQuick 2.0
import QtQuick.Controls 1.0

The main application window is represented with the ApplicationWindow control in the Qt Quick library:

Python
ApplicationWindow
{
...
}

There are four attributes of the main window that specify its size and title. The visible attribute specifies that the window should be shown right after launch.

Python
visible: true
width: 640
height: 480
title: qsTr("Minimal Qml")

Related project

Cross-Platform Data Backup Solution Development: Windows, Android, macOS, iOS

Discover the story of the client, partnering with Apriorit for over 18 years: weโ€™ve started with a simple Windows development project and now help them support a complex cross-platform data management solution.

Project details

QML Resource File

Next, letโ€™s look at the QML resource file for an application that consists of a sole QML file named main.qml. The file is written in XML with the RCC element as root and qresource as the element for resources of all groups. This project contains only one group thatโ€™s root (i.e. "/"). Larger projects may contain additional qresource elements for each resource subgroup (e.g. customControls, customWindows, etc.).

src/qml.qrc

XML
<RCC>
    <qresource prefix="โ„">
        <file>main.qml<โ„file>
    <โ„qresource>
<โ„RCC>

Application Code

The main entry point of the application provides the C++ code for displaying the Qt Quick user interface.

src/main.cpp

C
#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:โ„main.qml")));
    return app.exec();
}

The following code constructs a standard object for a Qt application:

C
QApplication app(argc, argv);

In the next section, the QML parsing object is initiated for the application. This object receives a string with the address of the main QML resource as the construction parameter. In this case, qrc:/main.qml is the address specified in the qml.qrc file. qrc is the default prefix for the QML resource, and /main.qml references the resource named main.qml in the root resource directory ("/").

C
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:โ„main.qml

The exec method starts the Qt application:

C
return app.exec();

Read also

Building Cross-Platform Apps With React Native: How to Run an App on Multiple Platforms from a Single Codebase

Explore a practical overview of top cross-platform development technologies and frameworks, along with a step-by-step React Native development guide with code examples from Apriorit experts.

Learn more

Project Files

Now weโ€™ll show you how to deploy a Qt Quick application using the CMake project format.

Letโ€™s look at the main project file. The first line sets the minimum version of CMake for the project. It then includes the Qt5 framework in the application as well as the src subdirectory so that CMake will search for the project file (CMakeLists.txt) there.

CMakeLists.txt

C
cmake_minimum_required(VERSION 2.8)
# 3rd party tools
find_package(Qt5 COMPONENTS Widgets Qml Quick REQUIRED)
# Directory with source code
add_subdirectory(src)

This is the project file for the executable:

src/CMakeLists.txt

C
include_directories(${Qt5Widgets_INCLUDE_DIRS} ${QtQml_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS} ${QtQml_DEFINITIONS} ${${Qt5Quick_DEFINITIONS}})
qt5_add_resources(QT_RESOURCES qml.qrc)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(PROJECT "MinimalQml")
project(${PROJECT})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++11 -fstrict-aliasing -pedantic-errors -pedantic -Wno-deprecated-declarations -Wno-unused-variable")
if(NOT DEFINED HEADERS)
    file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}โ„*.h)
endif()
if(NOT DEFINED SOURCES)
    file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}โ„*.cpp)
endif()
source_group("Header Files" FILES ${HEADERS})
source_group("Source Files" FILES ${SOURCES})
add_executable(${PROJECT} ${HEADERS} ${SOURCES} ${QT_RESOURCES})
target_link_libraries(${PROJECT}
    Qt5::Widgets
    Qt5::Qml
    Qt5::Quick
    )

The header files for the Qt project should be included so that the makefiles generated will specify them in the corresponding compilation commands. There Qt5Widgets stands the header files for the Qt Framework and Qt QML invokes special files for QML functions.

C
include_directories(${Qt5Widgets_INCLUDE_DIRS} ${QtQml_INCLUDE_DIRS})

The definitions as well as the Qt include files should be specified in the makefile compilation commands.

C
add_definitions(${Qt5Widgets_DEFINITIONS} ${QtQml_DEFINITIONS} ${${Qt5Quick_DEFINITIONS}})

The Qt framework requires code to be created from the Qt resource files. This is achieved using the special qt5_add_resources command. The QML resource file path relative to this file is passed as an argument:

C
qt5_add_resources(QT_RESOURCES qml.qrc)

For Qt projects with a graphical user interface, the compiler needs special parameters in order to compile:

C
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")

Qt programs require several additional compilation steps for moc, rcc, and uic:

C
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

The name of the project, which will be used as the name of the compiled executable, is specified using the set command, which receives the name of a variable (i.e. PROJECT) and its value.

C
set(PROJECT "MinimalQml")

The project command sets the current project within the CMake file and receives the value of the variable defined above:

C
project(${PROJECT})

The compiler flags for compiling C++ sources are set below. These flags set very strict compilation rules and help to detect and locate a lot of potential issues during compilation:

C
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++11 -fstrict-aliasing -pedantic-errors -pedantic -Wno-deprecated-declarations -Wno-unused-variable")

There are also conditional commands in CMake. The code below detects if there are HEADERS in the project. If header files were not previously set, the file(GLOB ... *.h) command creates a list of all header files in the current directory and passes them as header files of the current project. The same applies to the *.cpp sources that are stored in the SOURCES variable if that was not defined.

C
if(NOT DEFINED HEADERS)
    file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}โ„*.h)
endif()
if(NOT DEFINED SOURCES)
    file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}โ„*.cpp)
endif()

The source_group definition creates a group of files that are placed in some build systems (e.g. project sub-directories in Visual Studio).

C
source_group("Header Files" FILES ${HEADERS})
source_group("Source Files" FILES ${SOURCES})

This command states that the project results in an executable file. The first parameter receives the name of an executable file (which in this case is the same as the name of the project). The rest of the arguments are project sources, headers, and compiled Qt resources.

C
add_executable(${PROJECT} ${HEADERS} ${SOURCES} ${QT_RESOURCES})

The set of libraries that will be linked with the executable above is set with the target_link_libraries command. Like the command above, it receives the name of a projectโ€™s executable and a list of libraries. In this case, the project uses only Qt framework libraries.

C
target_link_libraries(${PROJECT}
    Qt5::Widgets
    Qt5::Qml
    Qt5::Quick
    )

Read also

Comprehensive Guide to End-to-End Cross-Platform Testing of React Native Apps

Streamline cross-platform testing with end-to-end approach. Ensure flawless user experience while saving QA resources and automating routine activities.

Learn more
Comprehensive Guide to End-to-End Cross-Platform Testing of React Native Apps

Building the Project

Now itโ€™s time for building with CMake.

You can build your program using the commands below. CMake takes a directory with the main projectโ€™s CMakeLists.txt file as an argument. Then it creates build files for GNU make, which builds an executable.

ShellScript
cd <PathToProject>
mkdir build && cd build
cmake ..
make

After a successful build, the binary MinimalQml will end up in build/src/.

ShellScript
.โ„srcโ„MinimalQml

Working on a Project with Qt Creator

You can probably avoid working with Qt Creator by creating a Qt QML C++ plugin with CMake, but we prefer going the traditional route.

If you want to know more about Qt Creator, you can find the official manual here. Now, letโ€™s look into how we can use Qt Creator with our Qt CMake example project.

Go to the Welcome tab in the main window and select Open Project (2).

Opening project in Qt Creator

In the dialog box that opens, locate the CMakeLists.txt file in the projectโ€™s root directory.

Locating the CMakeLists.txt file

Next, select the version of Qt framework that youโ€™ll use to compile the project. If you have multiple frameworks installed, itโ€™s possible to select several.

Selecting installed framework

After opening the project, its directory structure will be visible in the Projects panel in Qt Creator.

Project directory structure

The C++ source files are treated as usual by the editor.

View of the source files in the file editor

QRC resource files are opened as a resource tree.

View of QRC resource files

From the resource tree, you can open QML files for the project.

Opening the project QML files

Conclusion

We hope that this short tutorial has been useful and that you were able to take away some practical tips on using CMake with Qt. If you wish to learn more about Qt development, check out our article on Organizing RPC via QT. And if you ever need a team of competent C++ developers with years of experience, just send us your request for proposal. Weโ€™d be happy to talk.

Links

The source code for this project is available at our GitHub:

Qt Documentation:

CMake:

Letโ€™s build your cross-platform app together!

Describe your application idea and requirements and weโ€™ll get back to you with development ideas, strategies, and approaches.

Have a question?

Ask our expert!

Tell us about
your project

...And our team will:

  • Process your request within 1-2 business days.
  • Get back to you with an offer based on your project's scope and requirements.
  • Set a call to discuss your future project in detail and finalize the offer.
  • Sign a contract with you to start working on your project.

Do not have any specific task for us in mind but our skills seem interesting? Get a quick Apriorit intro to better understand our team capabilities.