Qt Embedded之一:简介与平台插件介绍
常见的嵌入式 Linux 图形界面有 Qt/Embedded、DirectFB、MicroWindows/NanoX、MiniGUI 和 OpenGUI 等,每个 GUI 都有各自不同特点和应用场合,在应用编程上也各不相同。而对于 Qt 来说,按照不同的图形界面来划分,分为四个版本:
- Win32 版:适用于 Windows 平台
- X11 版:适用于各种X系统的 Linux 和 Unix 平台
- Mac 版:适用于苹果的 MacOS
- Embedded 版:适用于具有帧缓冲(Frame buffer)的 Linux 平台
一、Qt/Embedded介绍
嵌入式 Linux 发行版上的 Qt 属于 Qt 的 Embedded Linux 分支平台(本文简称为 Qt/E)。Qt/E 在原始 Qt 的基础上,做了许多出色的调整以适合嵌入式环境。同桌面版的 Qt/X11 相比,嵌入式的 Qt/E 很节省内存,因为它不需要 X server 或是 Xlib 库,它在底层摒弃了 Xlib,采用 Framebuffer(帧缓冲)作为底层图形接口。Qt/E 的应用程序可以直接写内核帧缓冲,这避免开发者使用繁琐的 Xlib/Server 系统。下图所示比较了 Qt/E 与 Qt/X11 的架构区别。
二、编译流程
Qt/E 所面对的硬件平台较多,当开发人员需要在某硬件平台上移植 Qt/E 时,需要下载 Qt 源代码,利用交叉编译器编译出 Qt 库,接着需要将 Qt 库复制两份,一份放置在开发主机上,供编译使用,一份放在目标板上,供运行时动态加载使用。具体流程如下图所示。
开发要用到的东西:Qt 源代码 + 交叉编译器 + 一个灵活有创造性的大脑。
使用交叉编译器编译好的 Qt 库要在 LINUX 虚拟机和嵌入式开发板上各一份,有了这个库,我们就可以在上面尽情的编写 Qt 程序了!(如果需要触摸功能,记得要移植一个开源程序 tslib,它相当于底层驱动和上层应用的接口,有效的处理防抖误操作等触摸问题)。
程序搞定了,那我们如何编译呢?有多种方式:
- 直接使用Qt qmake工具编译;
- 使用Qt Creator软件直接编译;
- 市场上的一些第三方编译工具。
三、嵌入式 Linux 设备的平台插件
在嵌入式 Linux 系统上,可以使用多个平台插件:EGLFS,LinuxFB,DirectFB 或 Wayland。但是,这些插件的可用性取决于 Qt 的配置方式。
EGLFS 是许多主板上的默认插件。如果不合适,请使用QT_QPA_PLATFORM
环境变量来请求另一个插件。另外,对于快速测试,请使用-platform
具有相同语法的命令行参数。从 Qt 5.0 开始,Qt 不再用自己的窗口系统(QWS)。
3.1 EGLFS
EGLFS 是一个为了 Qt5 应用运行的平台插件,Qt5 运行在 EGL 和 OpenGL ES2.0 之上而且没有真实的视窗系统(比如 X11 Wayland)。对于 Qt Quick 2 和本地 OpenGL 应用,它也支持软件渲染视窗(例如 QWidget)。在 OpenGL 应用中,widget 的内容是使用 CPU 转化为图像渲染的,然后通过这个插件上传到纹理和混合。
对于包含 GPU 的现代嵌入式 Linux 设备,推荐使用 EGLFS 插件。EGLFS 强制第一个顶级窗口(QWidget 或者 QQucikView)为全屏。这个窗口也是根 widget 窗口,其它的顶级窗口(例如对话框)都是这个根窗口的子窗口。
如果有必要,EGLFS 可以通过使用以下环境变量进行配置:
环境变量 | 描述 |
---|---|
QT_QPA_EGLFS_FB | 覆盖帧缓冲设备。默认值为/dev/fb0 。在大多数嵌入式平台上,此变量不太重要,因为帧缓冲区仅用于查询显示尺寸等设置。但是,在某些设备上,此变量提供了指定在多个显示设置中使用哪个显示器的功能,类似于LinuxFB 中的fb 参数。 |
QT_QPA_EGLFS_WIDTH和 QT_QPA_EGLFS_HEIGHT |
包含屏幕的宽度和高度(以像素为单位)。虽然 EGLFS 尝试从帧缓冲设备/dev/fb0 确定尺寸,但这并不总是有效。可能需要手动指定尺寸。 |
QT_QPA_EGLFS_PHYSICAL_WIDTH和 QT_QPA_EGLFS_PHYSICAL_HEIGHT |
指定物理屏幕的宽度和高度(以毫米为单位)。有些平台没有/dev/fb0 或者查询失败的情况,这些值使用默认的 DPI=100 计算宽和高。 |
QT_QPA_EGLFS_DEPTH | 覆盖屏幕的颜色深度。在帧缓冲设备/dev/fb0 不可用或查询不成功的平台上,使用默认值32 。注意:此变量仅影响 QScreen 的色深值。它与 EGL 配置和 OpenGL 渲染所使用的颜色深度无关。 |
QT_QPA_EGLFS_SWAPINTERVAL | 默认的交换值为1 。这将会同步垂直刷新。例如,设置间隔为0 ,将会尽最快的方式运行不带任何同步。 |
QT_QPA_EGLFS_DEBUG | 设置后,一些调试信息会打印在调试输出上。 |
QT_QPA_EGLFS_INTEGRATION | 除了编译进的 hooks,能够提供设备或者厂商修改的可动态加载的插件。该环境变量指定一个特定的插件。 |
除了QT_QPA_EGLFS_DEBUG
,EGLFS 还支持 Qt 的现代分类日志系统。以下日志记录类别可用:
qt.qpa.egldeviceintegration
:为动态加载的后端启用日志记录。使用此类别可以检查正在使用的后端。qt.qpa.input
:启用 evdev 和 libinput 输入处理程序的调试输出。使用此类别来检查是否识别并打开了给定的输入设备。qt.qpa.eglfs.kms
:在 KMS/DRM 后端中启用详细日志记录。
EGLFS 清屏
在嵌入式平台上使用 EGLFS 做显示后端的时候,Qt 程序起来会清屏,也就是屏幕显示纯黑色,如果 Qt 程序比较大,在显示开机 logo 之后会有比较长的一段时间黑屏才能显示 Qt 的界面,用户体验不佳。解决方案请看:Qt eglfs程序清屏
3.2 LinuxFB
该插件通过 Linux 的 fbdev 子系统直接写入帧缓冲区。仅支持软件渲染的内容。请注意,在某些设置下,显示性能可能会受到限制。
但是,由于在 Linux 内核中已弃用 fbdev,因此从 Qt 5.9 开始,还提供了 DRM 哑缓存支持。要使用它,请将QT_QPA_FB_DRM
环境变量设置为非零值。设置后,只要系统支持哑缓存,/dev/fb0
就不会访问旧式帧缓存设备。取而代之的是,通过 DRM API 来设置呈现,类似于eglfs_kms
EGLFS 中的后端。输出经过双缓冲和页面翻转,也为软件渲染的内容提供了适当的垂直同步。
注意:使用哑缓冲区时,以下描述的选项均不适用,因为会自动查询物理和逻辑屏幕大小之类的属性。
该linuxfb
插件允许您通过QT_QPA_PLATFORM
环境变量或-platform
命令行选项指定其他设置。例如,QT_QPA_PLATFORM=linuxfb:fb=/dev/fb1
指定/dev/fb1
必须使用framebuffer设备而不是default fb0
。要指定多个设置,请用冒号(:)分隔。
设定值 | 描述 |
---|---|
fb=/dev/fbN |
指定帧缓冲设备。在多个显示器设置上,此设置使您可以在不同的显示器上运行该应用程序。当前,无法从一个Qt应用程序中使用多个帧缓冲区。 |
size= <宽度> *x *<高度> |
指定屏幕尺寸(以像素为单位)。该插件尝试从帧缓冲设备查询物理和逻辑的显示尺寸。但是,此查询可能并不总是会导致正确的结果。可能需要明确指定这些值。 |
mmsize= <宽度> *x *<高度> |
指定物理宽度和高度(以毫米为单位)。 |
offset= <宽度> *x *<高度> |
指定屏幕的左上角偏移量(以像素为单位)。默认位置为(0, 0) 。 |
nographicsmodeswitch |
指定不将虚拟终端切换到图形模式(KD_GRAPHICS )。通常,启用图形模式会禁用闪烁的光标和屏幕空白。但是,设置此参数后,这两个功能也会被跳过。 |
tty=/dev/ttyN |
覆盖虚拟控制台。仅在nographicsmodeswitch 未设置时使用。 |
从Qt 5.9 开始,就窗口大小调整策略而言,EGLFS 和 LinuxFB 的行为已同步:使用两个平台插件,第一个顶级窗口被迫覆盖整个屏幕。如果不希望这样做,请将QT_QPA_FB_FORCE_FULLSCREEN
环境变量设置为0
从早期的 Qt 版本恢复行为。
3.3 DirectFB
DirectFB 是一个轻量级的提供硬件图形加速,输入设备处理和抽象的图形库,它集成了支持半透明的视窗系统以及在 LinuxFramebuffer 驱动之上的多层显示。它是一个用软件封装当前硬件无法支持的图形算法来完成硬件加速的层。DirectFB 是为嵌入式系统而设计。它是以最小的资源开销来实现最高的硬件加速性能。
DirectFB 是图形API存在于 Frame Buffer 层之上与高级图形管理层如 GTK+ 等之下的。它可以以很小的系统资源占用来提供图形硬件加速功能,提供类如多路 a 通道渲染模型等高级图像操作。它不修改 Linux 内核,除了标准 C 库没有其他库的依赖。应用在了基于 Linux 系统的 DTV 显示系统的研发和其他有关 Linux 显示界面的项目上。支持市面上绝大多数显示卡,支持键盘、鼠标、遥控器、游戏手柄、触摸屏等输入设备。支持 JPEG、PNG、GIF、mpeg1/2、AVI、MOV、Flash、Video4Linux、DirectFB bitmap font 和 TrueType 等音视频文件和字体。
3.4 XCB
这是在常规桌面 Linux 平台上使用的 X11 插件。在某些提供 X 和 XCB 必要的开发文件的嵌入式环境中,此插件的功能与在常规 PC 桌面上一样。
注意:在某些设备上,X11 下没有 EGL 和 OpenGL 支持,因为 EGL 实现与 Xlib 不兼容。在这种情况下,XCB 插件是在没有 EGL 支持的情况下构建的,这意味着 Qt Quick 2 或其他基于 OpenGL 的应用程序无法与此平台插件一起使用。但是,它仍然可以用于运行软件渲染的应用程序(例如,基于 QWidget)。
通常,不建议在嵌入式设备上使用 XCB。像 EGLFS 这样的插件可能会提供更好的性能和硬件加速。
3.5 Wayland
Wayland 是一种轻巧的窗户系统。或更确切地说,它是客户端与显示服务器对话的协议。
Qt Wayland 提供了一个wayland
平台插件,允许 Qt 应用程序连接到 Wayland 合成器。
3.6 配置环境变量
以树莓派为例,配置环境变量,把以下内容追加到/etc/profile
最后,可以根据需求自行修改:
# 存放我们自己编写的qt程序的路径
export PATH=$PATH:/home/pi/qt5
# eglfs屏幕物理尺寸,单位毫米
export QT_QPA_EGLFS_PHYSICAL_WIDTH=84
export QT_QPA_EGLFS_PHYSICAL_HEIGHT=56
# eglfs屏幕分辨率
export QT_QPA_EGLFS_WIDTH=480
export QT_QPA_EGLFS_HEIGHT=320
# QT_QPA配置
export QT_QPA_PLATFORM=eglfs # 平台插件选择EGLFS
export QT_QPA_EGLFS_INTEGRATION=eglfs_kms # 设置插件为eglfs_kms,将使用KMS/DRM后端
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH # 链接库路径
export QTDIR=/usr/local/qt5pi
export QT_QPA_FONTDIR=$QTDIR/lib/fonts # 字体库路径
export QT_QPA_PLATFORM_PLUGIN_PATH=$QTDIR/plugins/ # 设置插件路径
export QT_QPA_EGLFS_HIDECURSOR=1 # 隐藏鼠标
export QT_QPA_EGLFS_ALWAYS_SET_MODE="1"
export QWS_MOUSE_PROTO=/dev/input/event0 # 触摸屏输入设备
export QML_IMPORT_PATH=$QTDIR/qml
export QML2_IMPORT_PATH=$QTDIR/qml
# QT_QPA通用配置
export QT_QPA_PLATFORM=eglfs:size=800x # 尺寸
export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/lib/qt5/plugins # Qt插件
export QT_QPA_FONTDIR=/usr/lib/fonts # 字体
export QT_QPA_GENERIC_PLUGINS=tslib # 触摸库
export QT_QPA_GENERIC_PLUGINS=evdevmouse:/dev/input/event # 鼠标插件
export QT_QPA_GENERIC_PLUGINS=evdevkeyboard:/dev/input/event # 键盘插件
四、字体
Qt 通常用于fontconfig
提供对系统字体的访问。如果fontconfig
不可用,则 Qt 将退回到使用QBasicFontDatabase
。在这种情况下,Qt 应用程序将在 Qt 的lib/fonts
目录中查找字体。Qt 将自动检测预渲染的字体和 TrueType 字体。可以通过设置QT_QPA_FONTDIR
环境变量来覆盖此目录。可以用代码加载不同的字体,例如思源黑体:
#ifdef Q_OS_WIN32
// 加载字体(思源黑体)
QString strFont = "./fonts/SourceHanSansCN-Normal-2.otf";
if(!QFile::exists(strFont)) {
strFont = "./fonts/msyh.ttf";
}
#else
// 加载字体(思源黑体)
QString strFont = "/usr/lib/fonts/SourceHanSansCN-Normal-2.otf";
if(!QFile::exists(strFont)) {
strFont = "/usr/lib/fonts/msyh.ttf";
}
#endif
int id = QFontDatabase::addApplicationFont(strFont); // 嵌入式字体加载指定路径
if(QFontDatabase::applicationFontFamilies(id).size())
{
QString msyh = QFontDatabase::applicationFontFamilies(id).at(0);
QFont font(msyh, 10);
font.setPointSize(16);
a.setFont(font);
}
有关支持的格式的更多信息,请参见《嵌入式Linux字体的Qt》。
注意: Qt不再在lib/fonts
目录中附带任何字体。这意味着由平台(系统映像)提供必要的字体。
五、Unix信号处理程序
面向主机平台的插件,像 eglfs 和 linuxfb 默认情况下捕获中断(安装信号处理器SIGINT
),暂停和继续(SIGTSTP
,SIGCONT
)和终止(SIGTERM
)。通过这种方式,键盘,光标终端,以及其他可能的图形状态可以在应用程序终止或被停用由于恢复kill
,或Ctrl+C
或Ctrl+Z
。(尽管只有在QT_QPA_ENABLE_TERMINAL_KEYBOARD
设置时才可以通过键盘终止或挂起,如上面“输入”部分所述)。但是,在某些情况下,捕获SIGINT
可能是不希望的,因为它可能与例如远程调试冲突。因此,提供了环境变量QT_QPA_NO_SIGNAL_HANDLER
以退出所有内置信号处理。