Jumpnow Technologies

home code consulting contact

RPi4 and Qt Development

17 May 2020

Some notes on developing Qt GUI applications with 64-bit Raspberry Pi4 systems.

Hardware

I am testing with the following displays

I have not tried any of the small TFT displays with the RPi4.

System Software

I am using a generic development/test system built with Yocto.

You can find instructions here or download an image here.

On these systems Qt defaults to using the linuxfb platform plugin.

A glance at the system

root@rpi4:~# uname -a
Linux rpi4 5.4.40-v8 #1 SMP PREEMPT Fri May 15 16:20:21 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux

root@rpi4:~# g++ --version
g++ (GCC) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

root@rpi4:~# qmake -v
QMake version 3.1
Using Qt version 5.14.1 in /usr/lib

root@rpi4:~# ls /usr/lib/plugins/platforms
libqeglfs.so  libqlinuxfb.so  libqminimal.so  libqminimalegl.so  libqoffscreen.so  libqvnc.so

root@rpi4:~# ls /usr/lib/libQt5*.5.14.1
/usr/lib/libQt53DAnimation.so.5.14.1
/usr/lib/libQt53DCore.so.5.14.1
/usr/lib/libQt53DExtras.so.5.14.1
/usr/lib/libQt53DInput.so.5.14.1
/usr/lib/libQt53DLogic.so.5.14.1
/usr/lib/libQt53DQuick.so.5.14.1
/usr/lib/libQt53DQuickAnimation.so.5.14.1
/usr/lib/libQt53DQuickExtras.so.5.14.1
/usr/lib/libQt53DQuickInput.so.5.14.1
/usr/lib/libQt53DQuickRender.so.5.14.1
/usr/lib/libQt53DQuickScene2D.so.5.14.1
/usr/lib/libQt53DRender.so.5.14.1
/usr/lib/libQt5Bluetooth.so.5.14.1
/usr/lib/libQt5Charts.so.5.14.1
/usr/lib/libQt5Concurrent.so.5.14.1
/usr/lib/libQt5Core.so.5.14.1
/usr/lib/libQt5DBus.so.5.14.1
/usr/lib/libQt5EglFSDeviceIntegration.so.5.14.1
/usr/lib/libQt5EglFsKmsSupport.so.5.14.1
/usr/lib/libQt5Gui.so.5.14.1
/usr/lib/libQt5Location.so.5.14.1
/usr/lib/libQt5Multimedia.so.5.14.1
/usr/lib/libQt5MultimediaQuick.so.5.14.1
/usr/lib/libQt5MultimediaWidgets.so.5.14.1
/usr/lib/libQt5Network.so.5.14.1
/usr/lib/libQt5Nfc.so.5.14.1
/usr/lib/libQt5OpenGL.so.5.14.1
/usr/lib/libQt5Positioning.so.5.14.1
/usr/lib/libQt5PositioningQuick.so.5.14.1
/usr/lib/libQt5PrintSupport.so.5.14.1
/usr/lib/libQt5Qml.so.5.14.1
/usr/lib/libQt5QmlModels.so.5.14.1
/usr/lib/libQt5QmlWorkerScript.so.5.14.1
/usr/lib/libQt5Quick.so.5.14.1
/usr/lib/libQt5QuickControls2.so.5.14.1
/usr/lib/libQt5QuickParticles.so.5.14.1
/usr/lib/libQt5QuickShapes.so.5.14.1
/usr/lib/libQt5QuickTemplates2.so.5.14.1
/usr/lib/libQt5QuickTest.so.5.14.1
/usr/lib/libQt5QuickWidgets.so.5.14.1
/usr/lib/libQt5Sensors.so.5.14.1
/usr/lib/libQt5SerialBus.so.5.14.1
/usr/lib/libQt5SerialPort.so.5.14.1
/usr/lib/libQt5Sql.so.5.14.1
/usr/lib/libQt5Svg.so.5.14.1
/usr/lib/libQt5Test.so.5.14.1
/usr/lib/libQt5VirtualKeyboard.so.5.14.1
/usr/lib/libQt5WebSockets.so.5.14.1
/usr/lib/libQt5Widgets.so.5.14.1
/usr/lib/libQt5Xml.so.5.14.1
/usr/lib/libQt5XmlPatterns.so.5.14.1

That is most but not all of the Qt packages in meta-qt5.

A notable exception is qtwebkit.

Because of the size and time to build I only include it when needed.

Running Qt Apps

The systems I am building do not include a display manager like Xorg or Wayland.

They are designed to run a single GUI process for applications such as an instrument or digital signage.

The Qt runtime does need to be told which platform plugin to use.

You can provide a -platform some-plugin command line argument when starting applications.

Or you can use an environment variable QT_QPA_PLATFORM which is what I have done.

root@rpi4:~# env | grep -i qt
QT_QPA_PLATFORM=linuxfb

The environment comes from /etc/profile.d/qt5-env.sh which in turn comes from this recipe in the Yocto build

meta-rpi64/recipes-qt/qt5-env/qt5-env.bb

in case you want to change the defaults.

Building Qt Apps on the RPi

The RPi4 is powerful enough that native building is convenient.

There are two examples applications already installed

You can recompile them directly on the device to verify the tools.

For this example I will build the qmlswipe app.

root@rpi4:~# git clone https://github.com/scottellis/qmlswipe.git
Cloning into 'qmlswipe'...
remote: Enumerating objects: 27, done.
remote: Counting objects: 100% (27/27), done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 27 (delta 11), reused 25 (delta 9), pack-reused 0
Unpacking objects: 100% (27/27), done.

root@rpi4:~# cd qmlswipe/

root@rpi4:~/qmlswipe# qmake
Info: creating stash file /home/root/qmlswipe/.qmake.stash

root@rpi4:~/qmlswipe# make
g++ -c -pipe --sysroot= -O2 -std=gnu++11 -Wall -Wextra -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQT_NO_DEBUG -DQT_QUICKCONTROLS2_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QMLMODELS_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -I/usr/include/QtQuickControls2 -I/usr/include/QtQuick -I/usr/include/QtGui -I/usr/include/QtQmlModels -I/usr/include/QtQml -I/usr/include/QtNetwork -I/usr/include/QtCore -I. -I/usr/lib/mkspecs/linux-g++ -o main.o main.cpp
/usr/bin/rcc -name qml qml.qrc -o qrc_qml.cpp
g++ -c -pipe --sysroot= -O2 -std=gnu++11 -Wall -Wextra -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQT_NO_DEBUG -DQT_QUICKCONTROLS2_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QMLMODELS_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -I/usr/include/QtQuickControls2 -I/usr/include/QtQuick -I/usr/include/QtGui -I/usr/include/QtQmlModels -I/usr/include/QtQml -I/usr/include/QtNetwork -I/usr/include/QtCore -I. -I/usr/lib/mkspecs/linux-g++ -o qrc_qml.o qrc_qml.cpp
g++ --sysroot= -Wl,-O1 -Wl,-rpath-link,/usr/lib -o qmlswipe main.o qrc_qml.o   /usr/lib/libQt5QuickControls2.so /usr/lib/libQt5Quick.so /usr/lib/libQt5Gui.so /usr/lib/libQt5QmlModels.so /usr/lib/libQt5Qml.so /usr/lib/libQt5Network.so /usr/lib/libQt5Core.so -lGLESv2 -lpthread   

root@rpi4:~/qmlswipe# ./qmlswipe
qml: Button 1 clicked
qml: Button 2 clicked
qml: Button 1 clicked
qml: Button 2 clicked
qml: Exit clicked


Cross-compiling Qt apps from the command line

Yocto can also build a toolchain capable of cross-compiling applications on a more powerful workstation.

The Yocto SDK is self-contained and easily installed, but over 1.5GB so I am not hosting it for download.

To build the toolchain, first setup the Yocto environment as normal

~$ source poky-dunfell/oe-init-build-env ~/rpi64/build

In local.conf specify the host machine architecture where the cross-tools will be used.

The choices are i686 or x86_64.

I am using SDKMACHINE = "x86_64".

Build the SDK like this

~/rpi64/build$ bitbake meta-toolchain-qt5

The resulting installation script can be found in ${TMPDIR}/deploy/sdk.

In my local.conf I have TMPDIR=/oe10/rpi64/tmp-dunfell, so the SDK installer can be found here

scott@fractal:~/dunfell-rpi64/build$ ls -l /oe10/rpi64/tmp-dunfell/deploy/sdk
total 943452
-rw-r--r-- 1 scott scott     52107 May 17 13:47 poky-glibc-x86_64-meta-toolchain-qt5-aarch64-raspberrypi4-64-toolchain-3.1.host.manifest
-rwxr-xr-x 1 scott scott 965711652 May 17 13:56 poky-glibc-x86_64-meta-toolchain-qt5-aarch64-raspberrypi4-64-toolchain-3.1.sh
-rw-r--r-- 1 scott scott     22781 May 17 13:46 poky-glibc-x86_64-meta-toolchain-qt5-aarch64-raspberrypi4-64-toolchain-3.1.target.manifest
-rw-r--r-- 1 scott scott    297772 May 17 13:46 poky-glibc-x86_64-meta-toolchain-qt5-aarch64-raspberrypi4-64-toolchain-3.1.testdata.json

Run the installer as root (I have not tried a non-root install)

Here I copied the *.sh installer to another machine and ran the script.

~$ sudo ./poky-glibc-x86_64-meta-toolchain-qt5-aarch64-raspberrypi4-64-toolchain-3.1.sh

The default install location is /opt/poky/<version>, but the script will ask.

I chose to install it in /opt/poky/rpi64-3.1.

To use the SDK, source the SDK environment using the provided script

~$ source /opt/poky/rpi64-3.1/environment-setup-aarch64-poky-linux

Here I will cross-compile the tspress app.

~$ git clone https://github.com/scottellis/tspress.git
Cloning into 'tspress'...
remote: Enumerating objects: 90, done.
remote: Total 90 (delta 0), reused 0 (delta 0), pack-reused 90
Unpacking objects: 100% (90/90), done.

~$ cd tspress

~/tspress$ qmake
Info: creating stash file /home/scott/tspress/.qmake.stash

~/tspress$ make
aarch64-poky-linux-g++  -mcpu=cortex-a72+crc+crypto -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -c -pipe  -O2 -pipe -g -feliminate-unused-debug-types  --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -O2 -Wall -Wextra -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -IGeneratedFiles -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtWidgets -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtGui -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtCore -IGeneratedFiles -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/mkspecs/linux-oe-g++ -o Objects/main.o main.cpp
aarch64-poky-linux-g++  -mcpu=cortex-a72+crc+crypto -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -c -pipe  -O2 -pipe -g -feliminate-unused-debug-types  --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -O2 -Wall -Wextra -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -IGeneratedFiles -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtWidgets -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtGui -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtCore -IGeneratedFiles -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/mkspecs/linux-oe-g++ -o Objects/tspress.o tspress.cpp
aarch64-poky-linux-g++  -mcpu=cortex-a72+crc+crypto -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -pipe  -O2 -pipe -g -feliminate-unused-debug-types  --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -O2 -Wall -Wextra -dM -E -o GeneratedFiles/moc_predefs.h /opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/mkspecs/features/data/dummy.cpp
/opt/poky/rpi64-3.1/sysroots/x86_64-pokysdk-linux/usr/bin/moc -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB --include /home/scott/qt/tspress/GeneratedFiles/moc_predefs.h -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/mkspecs/linux-oe-g++ -I/home/scott/qt/tspress -I/home/scott/qt/tspress/GeneratedFiles -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtWidgets -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtGui -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtCore -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/c++/9.3.0 -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/c++/9.3.0/aarch64-poky-linux -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/c++/9.3.0/backward -I/opt/poky/rpi64-3.1/sysroots/x86_64-pokysdk-linux/usr/lib/aarch64-poky-linux/gcc/aarch64-poky-linux/9.3.0/include -I/opt/poky/rpi64-3.1/sysroots/x86_64-pokysdk-linux/usr/lib/aarch64-poky-linux/gcc/aarch64-poky-linux/9.3.0/include-fixed -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include tspress.h -o GeneratedFiles/moc_tspress.cpp
aarch64-poky-linux-g++  -mcpu=cortex-a72+crc+crypto -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -c -pipe  -O2 -pipe -g -feliminate-unused-debug-types  --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -O2 -Wall -Wextra -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -IGeneratedFiles -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtWidgets -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtGui -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/include/QtCore -IGeneratedFiles -I/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/mkspecs/linux-oe-g++ -o Objects/moc_tspress.o GeneratedFiles/moc_tspress.cpp
aarch64-poky-linux-g++  -mcpu=cortex-a72+crc+crypto -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -fstack-protector-strong -Wl,-z,relro,-z,now --sysroot=/opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux -Wl,-O1 -o tspress Objects/main.o Objects/tspress.o Objects/moc_tspress.o   /opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/libQt5Widgets.so /opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/libQt5Gui.so /opt/poky/rpi64-3.1/sysroots/aarch64-poky-linux/usr/lib/libQt5Core.so -lGLESv2 -lpthread   

You can check that the resulting executable is for an ARM 64-bit architecture

~/tspress$ file tspress
tspress: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=e848dd21cab0ef2746042fee07bcd63b501b72ee, for GNU/Linux 3.14.0, with debug_info, not stripped

Copy it to the RPi4

~/tspress$ scp tspress root@192.168.10.205:/tmp
Warning: Permanently added '192.168.10.205' (ECDSA) to the list of known hosts.
tspress              

Then over on the RPi4, the tspress app should run fine.

Creating Bitbake recipes for your Qt apps

When you have completed development you will want Yocto to build and install your app as part of the system build.

Under the meta-rpi64/recipes-qt directory are the recipes for the example applications qmlswipe and tspress.

You can use either as templates for your own application.

Check the poky-dunfell/meta-qt5 layer for additional examples.