Skip to content

Commit

Permalink
ch16: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
e8johan committed Aug 23, 2021
1 parent ef23b23 commit e857879
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 102 deletions.
46 changes: 14 additions & 32 deletions docs/ch16-qtcpp/boilerplate.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# A Boilerplate Application

The best way to understand Qt is to start from a small demonstration application. This application creates a simple `"Hello World!"` string and writes it into a file using Unicode characters.
The best way to understand Qt is to start from a small example. This application creates a simple `"Hello World!"` string and writes it into a file using Unicode characters.

```cpp
#include <QCoreApplication>
Expand Down Expand Up @@ -38,38 +38,21 @@ int main(int argc, char *argv[])
}
```
The simple example demonstrates the use of file access and the correct way of writing text into a file using text codecs via the text stream. For binary data, there is a cross-platform binary stream called `QDataStream`. The different classes we use are included using their class name. Another possibility would be to use a module and class name e.g. `#include <QtCore/QFile>`. For the lazy, there is also the possibility to include a whole module using `#include <QtCore>`. E.g. in `QtCore` you have the most common classes used for an application, which are not UI dependent. Have a look at the [QtCore class list](http://doc.qt.io/qt-5/qtcore-module.html) or the [QtCore overview](http://doc.qt.io/qt-5/qtcore-index.html).
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](http://doc.qt.io/qt-5/qtcore-module.html) or the [QtCore overview](http://doc.qt.io/qt-5/qtcore-index.html).
You build the application using qmake and make. QMake reads a project file and generates a Makefile which then can be called using make. The project file is platform independent and qmake 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.
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.
```sh
# build an application
TEMPLATE = app
# use the core module and do not use the gui module
QT += core
QT -= gui
TODO
# name of the executable
TARGET = CoreApp
# allow console output
CONFIG += console
# for mac remove the application bundling
macx {
CONFIG -= app_bundle
}
# sources to be build
SOURCES += main.cpp
```

We will not go into depth into this topic. Just remember Qt uses project files for projects and qmake generates the platform-specific makefiles from these project files.
We will not go into depth into this topic. Just remember Qt uses CMake's `CMakeLists.txt` files to generate platform-specific makefiles, that are then used to build a project.

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 would need an event loop which waits for user input and somehow schedules re-draw operations. So here follows the same example now uses a desktop button to trigger the writing.
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 signal/slots for the user input, e.g. the button click. The signal/slot mechanism normally needs an object instance as you will see shortly.
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.

```cpp
#include <QtCore>
Expand All @@ -90,7 +73,7 @@ int main(int argc, char** argv)
}
```
In the `main` function we simply create the application object and start the event loop using `exec()`. For now, the application sits in the event loop and waits for user input.
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.
```cpp
int main(int argc, char** argv)
Expand All @@ -103,13 +86,11 @@ int main(int argc, char** argv)
}
```

Qt offers several UI technologies. For this example, we use the Desktop Widgets user interface library using pure Qt C++. We create the main window which will host a push button to trigger the functionality and also the main window will host our core functionality which we know from the previous example.


Using Qt, you can build user interfaces in both QML and Widgets. In this book we focus on QML, but in this chapter, we will look at Widgets. This lets us create the program only C++.

![image](./images/storecontent.png)

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 a child of the main window and becomes a widget inside the window.
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.

```cpp
#ifndef MAINWINDOW_H
Expand All @@ -131,7 +112,9 @@ private:
#endif // MAINWINDOW_H
```

Additionally, we define a public slot called `storeContent()` which shall be called when the button is clicked. A slot is a C++ method which is registered with the Qt meta object system and can be dynamically called.
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.

The purpose of the `storeContent()` slot is that is is called when the button is clicked. Let's make that happen!

```cpp
#include "mainwindow.h"
Expand Down Expand Up @@ -165,5 +148,4 @@ void MainWindow::storeContent()

```
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. As simple as this, objects communicate via signal and slots with loose coupling.
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.
137 changes: 71 additions & 66 deletions docs/ch16-qtcpp/build-system.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,90 @@
# Build Systems

Building software reliably on different platforms can be a complex task. You will encounter different environments with different compilers, paths, and library variations. The purpose of Qt is to shield the application developer from these cross-platform issues. For this Qt introduced the `qmake` build file generator. `qmake` operates on a project file with the ending `.pro`. This project file contains instructions about the application and the sources to be used. Running qmake on this project file will generate a `Makefile` for you on Unix and MacOS and even under windows if the MinGW compiler toolchain is being used. Otherwise, it may create a visual studio project or an Xcode project.
Building software reliably across different platforms can be a complex task. You will encounter different environments with different compilers, paths, and library variations. The purpose of Qt is to shield the application developer from these cross-platform issues. Qt relies on [CMake](https://cmake.org/) to convert `CMakeLists.txt` project files to platform specific make files, which then can be built using the platform specific tooling.

:: note
::
:: Qt comes with three different build systems. The original Qt build system
:: was called `qmake`. Another Qt specific build system is `QBS` which uses a
:: declarative approach to describing the build sequence. Since version 6, Qt
:: has shifted from `qmake` to CMake as the official build system.

A typical build flow in Qt under Unix would be:

```sh
edit myproject.pro
qmake // generates Makefile
vim CMakeLists.txt
cmake . // generates Makefile
make
```

Qt allows you also to use shadow builds. A shadow build is a build outside of your source code location. Assume we have a myproject folder with a `myproject.pro` file. The flow would be like this:
With Qt you are encouraged to use shadow builds. A shadow build is a build outside of your source code location. Assume we have a myproject folder with a `CMakeLists.txt` file. The flow would be like this:

```sh
mkdir build
cd build
qmake ../myproject/myproject.pro
cmake ..
```

We create a build folder and then call cmake from inside the build folder with the location of our project folder. This will set up the makefile in a way that all build artifacts are stored under the build folder instead of inside our source code folder. This allows us to create builds for different qt versions and build configurations at the same time and also it does not clutter our source code folder which is always a good thing.

When you are using Qt Creator it does these things behind the scenes for you and you do not have to worry about these steps in most cases. For larger projects and for a deeper understanding of the flow, it is recommended that you learn to build your Qt project from the command line to ensure that you have full control over what is happening.

## CMake

CMake is a tool created by Kitware. Kitware is very well known for their 3D visualization software VTK and also CMake, the cross-platform makefile generator. It uses a series of `CMakeLists.txt` files to generate platform-specific makefiles. CMake is used by the KDE project and as such has a special relationship with the Qt community and since version 6, it is the preferred way to build Qt projects.

The `CMakeLists.txt` is the file used to store the project configuration. For a simple hello world using QtCore the project file would look like this:

```cmake
// ensure cmake version is at least 3.16.0
cmake_minimum_required(VERSION 3.16.0)
// defines a project with a version
project(foundation_tests VERSION 1.0.0 LANGUAGES CXX)
// pick the C++ standard to use, in this case C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
// tell CMake to run the Qt tools moc, rcc, and uic automatically
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
// configure the Qt 6 modules core and test
find_package(Qt6 COMPONENTS Core REQUIRED)
find_package(Qt6 COMPONENTS Test REQUIRED)
// define an executable built from a source file
add_executable(foundation_tests
tst_foundation.cpp
)
// tell cmake to link the executable to the Qt 6 core and test modules
target_link_libraries(foundation_tests PRIVATE Qt6::Core Qt6::Test)
```

We create a build folder and then call qmake from inside the build folder with the location of our project folder. This will set up the makefile in a way that all build artifacts are stored under the build folder instead of inside our source code folder. This allows us to create builds for different qt versions and build configurations at the same time and also it does not clutter our source code folder which is always a good thing.
This will build a `foundations_tests` executable using `tst_foundation.cpp` and link against the Core and Test libraries from Qt 6. You will find more examples of CMake files in this book, as we use CMake for all C++ based examples.

CMake is a powerful, a complex, tool and it takes some time to get used to the syntax. CMake is very flexible and really shines in large and complex projects.

## References


* [CMake Help](http://www.cmake.org/documentation/) - available online but also as QtHelp format


* [Running CMake](http://www.cmake.org/runningcmake/)


* [KDE CMake Tutorial](https://techbase.kde.org/Development/Tutorials/CMake)


* [CMake Book](http://www.kitware.com/products/books/CMakeBook.html)


* [CMake and Qt](http://www.cmake.org/cmake/help/v3.0/manual/cmake-qt.7.html)

When you are using Qt Creator it does these things behind the scenes for you and you do not have to worry about these steps usually. For larger projects and for a deeper understanding of the flow, it is recommended that you learn to build your Qt project from the command line.

## QMake

Expand Down Expand Up @@ -125,62 +189,3 @@ QMake based projects are normally the number one choice when you start programmi


* [QMake Variables](http://doc.qt.io/qt-5//qmake-variable-reference.html) - Variables like TEMPLATE, CONFIG, QT is explained here

## CMake

CMake is a tool created by Kitware. Kitware is very well known for their 3D visualization software VTK and also CMake, the cross-platform makefile generator. It uses a series of `CMakeLists.txt` files to generate platform-specific makefiles. CMake is used by the KDE project and as such has a special relationship with the Qt community.

The `CMakeLists.txt` is the file used to store the project configuration. For a simple hello world using QtCore the project file would look like this:

```cmake
// ensure cmake version is at least 3.0
cmake_minimum_required(VERSION 3.0)
// adds the source and build location to the include path
set(CMAKE_INCLUDE_CURRENT_DIR ON)
// Qt's MOC tool shall be automatically invoked
set(CMAKE_AUTOMOC ON)
// using the Qt5Core module
find_package(Qt5Core)
// create excutable helloworld using main.cpp
add_executable(helloworld main.cpp)
// helloworld links against Qt5Core
target_link_libraries(helloworld Qt5::Core)
```

This will build a helloworld executable using main.cpp and linked agains the external Qt5Core library. The build file can be modified to be more generic:

```cmake
// sets the PROJECT_NAME variable
project(helloworld)
cmake_minimum_required(VERSION 3.0)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
find_package(Qt5Core)
// creates a SRC_LIST variable with main.cpp as single entry
set(SRC_LIST main.cpp)
// add an executable based on the project name and source list
add_executable(${PROJECT_NAME} ${SRC_LIST})
// links Qt5Core to the project executable
target_link_libraries(${PROJECT_NAME} Qt5::Core)
```

You can see that CMake is quite powerful. It takes some time to get used to the syntax. In general, it is said that CMake is better suited for large and complex projects.

## References


* [CMake Help](http://www.cmake.org/documentation/) - available online but also as QtHelp format


* [Running CMake](http://www.cmake.org/runningcmake/)


* [KDE CMake Tutorial](https://techbase.kde.org/Development/Tutorials/CMake)


* [CMake Book](http://www.kitware.com/products/books/CMakeBook.html)


* [CMake and Qt](http://www.cmake.org/cmake/help/v3.0/manual/cmake-qt.7.html)

7 changes: 3 additions & 4 deletions docs/ch16-qtcpp/qtcpp.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# Qt and C++

Qt is a C++ toolkit with an extension for QML and Javascript. There exist many language bindings for Qt, but as Qt is developed in C++, the spirit of C++ can be found throughout the classes. In this section, we will look at Qt from a C++ perspective to build a better understanding of how to extend QML with native plugins developed using C++. Through C++, it is possible to extend and control the execution environment provided to QML.
Qt is a C++ toolkit with an extension for QML and Javascript. There exist many language bindings for Qt, but as Qt itself is developed in C++. The spirit of C++ can be found throughout the classes. In this section, we will look at Qt from a C++ perspective to build a better understanding of how to extend QML with native plugins developed using C++. Through C++, it is possible to extend and control the execution environment provided to QML.

![image](./images/yourapplication.png)


This chapter will, just as Qt, require the reader to have some basic knowledge of C++. Qt does not rely on advanced C++ features, and I generally consider the Qt style of C++ to be very readable, so do not worry if you feel that your C++ knowledge is shaky.
This chapter will, just as Qt, require the reader to have some basic knowledge of C++. Qt does not rely on advanced C++ features and I generally consider the Qt style of C++ to be very readable, so do not worry if you feel that your C++ knowledge is shaky.

## Qt C++

Expand All @@ -15,7 +14,7 @@ Qt uses this meta information to enable a very loosely bound callback concept us

## Qt for Python

The introspection features are also used to create dynamic language bindings, making it possible to expose a C++ object instance to QML and making C++ functions callable from Javascript. Other bindings for Qt C++ exist and besides the standard Javascript binding, a popular one is the Python binding called [PyQt](http://www.riverbankcomputing.co.uk/software/pyqt/intro).
The introspection features are also used to create dynamic language bindings, making it possible to expose a C++ object instance to QML and making C++ functions callable from Javascript. Other bindings for Qt C++ exist and besides the standard Javascript binding, the official one is the Python binding called [PySide6](https://www.qt.io/qt-for-python).

## Cross Platform

Expand Down

0 comments on commit e857879

Please sign in to comment.