---
description: >-
  Let´s take a look how you can develop and cross compile Qt applications for
  your Raspberry Pi.
---

# Remote Qt development

If you want to develop a graphical user interface for the Raspberry Pi, in most cases you end up with a solution that is realized with Qt. This is a universal C ++ library for creating graphical interfaces for various operating systems, such as B. Windows, Linux or Android.

Unfortunately, a Raspberry Pi does not offer a lot of computing power, which is why the development of graphical interfaces on the Raspberry Pi is rather unattractive. A much more elegant way would be if the program was developed on a host PC and then copied and executed on the Raspberry Pi via remote connection.

And that's exactly what this guide should be about. I will show how the Raspberry Pi and an Ubuntu host system can be used to develop Qt applications for the Raspberry Pi on the host system, which are then run remotely on the Raspberry Pi.

## Prepare the Raspberry Pi

For a smooth process, the operating system (here Raspbian) on the Raspberry Pi must be updated. To do this, the file `/etc/apt/sources.list` must first be adjusted and Debian source packages must be permitted. Add this to the file:

```
deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
```

Then the package list, the packages and the operating system of the Raspberry Pi are updated:

```
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo reboot
$ sudo rpi-update
$ sudo reboot
```

Then the missing packages are installed:

```
$ sudo apt-get build-dep qt5-qmake
$ sudo apt-get build-dep libqt5gui5
$ sudo apt-get build-dep libqt5webengine-data
$ sudo apt-get build-dep libqt5webkit5
$ sudo apt-get install libudev-dev libinput-dev libts-dev libxcb-xinerama0-dev libxcb-xinerama0 gdbserver
```

And finally, a folder for the Qt installation and some additional symlinks are created:

```
$ sudo mkdir /usr/local/RaspberryQt
$ sudo chown -R pi:pi /usr/local/RaspberryQt
$ ln -s /opt/RaspberryQt/sysroot/opt/vc/lib/libEGL.so /opt/RaspberryQt/sysroot/usr/lib/arm-linux-gnueabihf/libEGL.so.1.0.0 
$ ln -s /opt/RaspberryQt/sysroot/opt/vc/lib/libGLESv2.so /opt/RaspberryQt/sysroot/usr/lib/arm-linux-gnueabihf/libGLESv2.so.2.0.0 
$ ln -s /opt/RaspberryQt/sysroot/opt/vc/lib/libEGL.so /opt/RaspberryQt/sysroot/opt/vc/lib/libEGL.so.1 
$ ln -s /opt/RaspberryQt/sysroot/opt/vc/lib/libGLESv2.so /opt/RaspberryQt/sysroot/opt/vc/lib/libGLESv2.so.2 
```

The configuration of the Raspberry Pi is now complete. It continues with the host ...

## Compile Qt for the Raspberry Pi

For the host system I used an Ubuntu 18.04 LTS in a virtual machine.&#x20;

{% embed url="https://releases.ubuntu.com/18.04.4/" %}

Here, too, the package sources and packages must first be updated and a few additional packages installed.

```
$ sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu xenial-security main"
$ sudo apt update
$ sudo apt install libjasper-dev
$ sudo apt-get install -y git cmake build-essential pkg-config libjpeg-dev libtiff5-dev libpng-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev libatlas-base-dev gfortran wget unzip libz-dev zlib1g-dev gcc g++ git bison python gperf gdb-multiarch qt5-default texinfo make python3-dev
```

Then the working directories are created and the rights configured accordingly:

```
$ sudo mkdir /opt/RaspberryQt
$ sudo mkdir /opt/RaspberryQt/build
$ sudo mkdir /opt/RaspberryQt/sysroot /opt/RaspberryQt/sysroot/usr /opt/RaspberryQt/sysroot/opt
$ sudo chown -R 1000:1000 /opt/RaspberryQt
```

An SSH key for the Raspberry Pi is then created for easier work, which avoids annoying password entries when copying the data.

```
$ ssh-keygen -t rsa -C root@<IP Raspberry Pi> -P "" -f ~/.ssh/rpi_root_id_rsa
$ ssh-keygen -t rsa -C pi@<IP Raspberry Pi> -P "" -f ~/.ssh/rpi_pi_id_rsa
$ cat ~/.ssh/rpi_root_id_rsa.pub | ssh root@<IP Raspberry Pi> 'cat >> .ssh/authorized_keys && chmod 640 .ssh/authorized_keys'
$ cat ~/.ssh/rpi_pi_id_rsa.pub | ssh pi@<IP Raspberry Pi> 'cat >> .ssh/authorized_keys && chmod 640 .ssh/authorized_keys'
```

The necessary files can then be downloaded.

```
$ cd /opt/RaspberryQt
$ git clone https://github.com/raspberrypi/tools
$ wget https://download.qt.io/archive/qt/5.12/5.12.8/single/qt-everywhere-src-5.12.8.tar.xz
$ wget https://raw.githubusercontent.com/riscv/riscv-poky/master/scripts/sysroot-relativelinks.py
```

The files are a cross compiler for the Raspberry Pi, the Qt version used (here 5.12.8) and a Python script to replace symbolic links of files with relative links (will be needed later).

Then the Qt version is unpacked and the name of the compiler used (here `arm-linux-gnueabihf`) is adjusted in the Qt configuration file.

```
$ tar xf qt-everywhere-src-5.12.8.tar.xz
$ cp -R qt-everywhere-src-5.12.8/qtbase/mkspecs/linux-arm-gnueabi-g++ qt-everywhere-src-5.10.1/qtbase/mkspecs/linux-arm-gnueabihf-g++
$ sed -i -e 's/arm-linux-gnueabi-/arm-linux-gnueabihf-/g' qt-everywhere-src-5.12.8/qtbase/mkspecs/linux-arm-gnueabihf-g++/qmake.conf
```

Now the libraries used by the target (here the Raspberry Pi) Pi are still required. These can be downloaded from the Raspberry Pi using `rsync`.

```
$ rsync -avz root@<IP vom Raspberry Pi>:/lib sysroot
$ rsync -avz root@<IP vom Raspberry Pi>:/usr/include sysroot/usr
$ rsync -avz root@<IP vom Raspberry Pi>:/usr/lib sysroot/usr
$ rsync -avz root@<IP vom Raspberry Pi>:/opt/vc sysroot/opt
```

It may take a few minutes to download. These files will later be required for the Qt build process, since Qt requires some standard libraries from the target system (here the Raspberry Pi).

Before the files can be used, the symbolic links that still point to the file system of the Raspberry Pi must be replaced by relative links from the new `sysroot` directory.&#x20;

```
/opt/RaspberryQt/sysroot-relativelinks.py /opt/RaspberryQt/sysroot
```

Finally, Qt is configured.

```
$ cd /opt/RaspberryQt/build
$ ../qt-everywhere-src-5.12.8/configure -opengl es2 -device linux-rasp-pi3-g++ -device-option CROSS_COMPILE=/opt/RaspberryQt/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf- -sysroot /opt/RaspberryQt/sysroot -prefix /usr/local/RaspberryQt -opensource -confirm-license -no-gbm -skip qtscript -nomake tests -nomake examples -make libs -pkg-config -no-use-gold-linker -v
```

During configuration, the type of the target must be specified via the parameter of the argument `-device`. The following table applies to the parameters.

| Target                             | Option                    |
| ---------------------------------- | ------------------------- |
| Raspberry Pi 1 (+ Zero and Zero W) |  _linux-rasp-pi-g++_      |
| Raspberry Pi 2                     |  _linux-rasp-pi2-g++_     |
| Raspberry Pi 3                     |  _linux-rasp-pi3-g++_     |
| Raspberry Pi 3 with VC4 driver     |  _linux-rasp-pi3-vc4-g++_ |

Once the configuration is complete, Qt can be compiled and installed.

```
$ make -j4
$ make install
```

The number of cores used is specified via parameter `-j4`, which can speed up the process. With a core, the entire process takes a good 3-4 hours (depending on the system). The fully compiled Qt libraries are then copied to the Raspberry Pi using `rsync`.

```
$ cd /opt/RaspberryQt
$ rsync -avz sysroot/usr/local/RaspberryQt root@<IP des Raspberry Pi>:/usr/local
```

The Qt libraries and the Raspberry Pi are now ready for use. Qt applications can now be created, compiled and executed on the Raspberry Pi. The next step is to create a new GDB, because the GDB from the Raspberry Pi Toolchain does not have any support for the Python programming language and therefore cannot be used in the Qt Creator.

## Compiling GDB

Before GDB can be compiled, a few additional packages must be installed:

```
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install texinfo gcc gcc-c++ make python3-devel wget
```

The sources for GDB can be downloaded directly from the FTP server of the GNU project by using the `wget` command:

```
$ wget https://ftp.gnu.org/gnu/gdb/gdb-9.1.tar.xz
$ tar xf gdb-9.1.tar.xz
$ mkdir gdb-9.1/build
```

The installed Python version and the corresponding Python libraries are required to create GDB:

```
$ export PYTHON=python3
$ export PYTHON_LIBDIR=$("${PYTHON}" -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
```

GDB can now be configured and created:

```
$ cd gdb-9.1/build
$ ../configure --prefix=/home/kampi/GDB/bin --target=arm-linux-gnueabihf --with-python=${PYTHON} LDFLAGS="-L${PYTHON_LIBDIR}"
$ make -j4
$ make -C gdb install
```

The GDB version created is saved in the `bin/` directory at the end of the compilation process.

This would complete the tools. I'll show you in the next step how you can set up the Qt Creator.

## Configure Qt Creator for the host system

The Qt Creator is best suited for the development of Qt applications. Since the installation via the official Ubuntu package sources didn't work for me and the Qt Creator always throw some errors me, I downloaded and ran the Qt installer directly from the website.

```
$ cd ~/Downloads
$ sudo chmod +x qt-unified-linux-x64-3.0.6-online.run
$ ./qt-unified-linux-x64-3.0.6-online.run
```

{% hint style="info" %}
This step installs the complete Qt libraries, etc. on the host system and not just the Qt Creator.
{% endhint %}

After installation, the Qt Creator must still be configured for the Raspberry Pi. To do this, the Creator is started and the menu _Tools_ → _Settings_ is opened. Then the Devices submenu is opened and click on _Add..._ to add a new _Generic Linux device_.

The _Start Wizard_ button confirms the selection and creates a new device. The key file already created can be used for the SSH connection. The setup of the new device is completed with _Next_ → _Complete_. The Qt Creator then tests the settings and tries to connect to the device.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5m_ieCu_YDXm6WCUpi%2F-M5mbHGQHXSkttcB3Ktl%2Fimage.png?alt=media\&token=06de7539-6930-448f-825d-4d954a9c3571)

If the test was successful, the window can be closed. The new device is now listed in the device list.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5m_ieCu_YDXm6WCUpi%2F-M5mbEmteQ1Of33CpETo%2Fimage.png?alt=media\&token=e25806f3-90c4-404f-8e25-7375675e7a3f)

The kit, i.e. the compiler, debugger and source collection, is then created. The _Settings_ menu is opened again via _Tools_ and the _Kits_ item is selected. In the _Compiler_ tab, the _Add_ button adds an entry for a C and a C ++ compiler (both can be found under _GCC_).

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5qxvRpkJfYJBdaPQJO%2F-M5qxy6E8FJ6JdvU02Bu%2Fimage.png?alt=media\&token=b27873db-f412-4912-a57e-06953b367f7a)

| Compiler | Path                                                                                                                                                                                                                                     |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| C        | `/home/kampi/Toolchain/Raspberry Pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-gcc/opt/RaspberryQt/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-gcc` |
| C++      | `/home/kampi/Toolchain/Raspberry Pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-g++/opt/RaspberryQt/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-g++` |

The entries are then confirmed by clicking _Apply_.

Next, the Qt version for the Raspberry Pi is created. To do this, the _Qt Versions_ tab is opened and the _Add_ button is used to create a new entry. A file browser opens with which the path of the created `qmake` is selected.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5rKW3IYL3YFvD5Uu5t%2F-M5rgZQh7mbbeEPYDN1F%2Fimage.png?alt=media\&token=3d32b33d-3aea-47ae-a2be-ca2a08825470)

The entry is confirmed by clicking the `Apply` button.

In the last step, a new debugger is created. To do this, the _Debugger_ tab is opened and a new entry is created using the _Add_ button and filled in with the path of the debugger created.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5r-BBvRGdPCnM8qoNw%2F-M5rIrGI7Z1LenaJ_TeT%2Fimage.png?alt=media\&token=8e18f57d-b8e3-47f7-abf1-485f16d030c3)

Now the kit can be created. The _Kits_ tab is opened and a new kit is created using the _Add_ button. The mask is now filled in according to the screenshot.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M6-rctnZRDWNScD_-nZ%2F-M6-rlBY3Mv_eBLsq-i9%2Fimage.png?alt=media\&token=e7a8664c-1e87-4e04-a6dc-9c15d90b2114)

Here too, the entry is confirmed by clicking on _Apply_. The window can then be closed with _OK_.

## Create the first application

Now that all tools are together, a sample project should be developed on the Ubuntu host system and executed remotely on the Raspberry Pi. Via _File_ → _New..._ a new Qt-Widgets project is created. The individual entries are confirmed with _Next_. When selecting the kit, the Raspberry Pi Kit that has just been created is selected as the kit.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5mdbpd8IWqkQdN0MEc%2F-M5mdk7snt8giU0S713Z%2Fimage.png?alt=media\&token=4142017b-81f8-4c25-9bee-14705e05f814)

All other entries can be confirmed directly with _Next_ and the wizard can then be closed with _Close_.

Next, the deployment rule must be adjusted in the project file, since the user pi is not allowed to access the default directory `/opt` by default. Therefore the path in `/home/pi` is changed.

```
target.path = /home/pi/$${TARGET}/bin
INSTALLS += target
```

In the created project, two environment variables must then be set for the target, i.e. the Raspberry Pi. To do this, the _Projects_ tab is opened and the item _Execute_ the current kit is selected. The variables `DISPLAY =: 0` and `XAUTHORITY = /home/pi/.Xauthority` are created and set in the submenu _Execution Environment_.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5mdbpd8IWqkQdN0MEc%2F-M5metx_RcyoSNKFmO0K%2Fimage.png?alt=media\&token=7daea7e2-c4d3-40e5-a8fd-fac418fc4c7c)

In addition, the command line parameters must be set to `-platform xcb`.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5mdbpd8IWqkQdN0MEc%2F-M5mf3zfveFa1IpKcEfl%2Fimage.png?alt=media\&token=516af934-dc94-409b-8110-79936cc247b4)

{% hint style="info" %}
In the standard configuration, Qt for Embedded Linux uses _eglfs_ to render the windows, which means that no display server such as _X11_ or _Wayland_ is required.&#x20;

Therefore, the applications always start in full screen mode and cannot be minimized or pushed into the background. If egl is to be used, the size of the screen has to be defined. This is done using the environment variables `QT_QPA_EGLFS_PHYSICAL_WIDTH`, `QT_QPA_EGLFS_PHYSICAL_HEIGHT`, `QT_QPA_EGLFS_HEIGHT` and `QT_QPA_EGLFS_WIDTH`.

In the case of the official Raspberry Pi screen, the size of the screen can be found in the official drawing. The entered values ​​correspond to the size of the screen in **millimeters** and **pixels**! For other screens, the values ​​must be adjusted accordingly.

In addition, two paths need to be adjusted on the Raspberry Pi:

`$ cd /opt/vc/lib` \
`$ sudo ln -s libbrcmEGL.so libEGL.so.1` \
`$ sudo ln -s libbrcmGLESv2.so libGLESv2.so.2` \
`$ echo "export LD_LIBRARY_PATH=/opt/vc/lib" >> ~/.bashrc`
{% endhint %}

This means that the setup is ready for use and nothing stands in the way of developing graphic applications on the PC for the Raspberry Pi. With a click on the green arrow in the side bar or with the key combination _Ctrl_ + _R_ the project is created and the application is started a few seconds later on the Raspberry Pi.

![](https://3264410389-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5mWcgJr4R8y6CWZlvZ%2F-M5mdbpd8IWqkQdN0MEc%2F-M5mfv3s3YgAt_FGTUmQ%2Fimage.png?alt=media\&token=78571ba4-3ea5-4e68-bc1b-2a1606d12c4b)

## Add additional modules

The installation carried out is a minimal installation, in which not all modules are present (e.g. the Qt charts are missing). These modules can be installed later by downloading the sources of the respective module from the submodules directory of the respective Qt version (hereinafter carried out for Qt charts):

```
$ wget http://download.qt.io/official_releases/qt/5.12/5.12.8/submodules/qtcharts-everywhere-src-5.12.8.tar.xz
$ tar xf qtcharts-everywhere-src-5.12.8.tar.xz
```

After unpacking, you change to the directory and start the installation. The path of the created `qmake` is required for this:

```
$ cd qtcharts-everywhere-src-5.12.8
$ /opt/RaspberryQt/sysroot/usr/local/RaspberryQt/bin/qmake
$ make
$ make install
```

The created libraries must then be copied to the Raspberry Pi using `rsync`.

```
$ rsync -avz /opt/RaspberryQt/sysroot/usr/local/RaspberryQt root@$<IP des Raspberry Pi>:/usr/local
```

And the additional module is ready for use.

I have created some bash scripts that contain the complete installation procedure (both for the Raspberry Pi and for the host system). You can download both scripts and the sample project can be found in my GitLab repository.
