为您的嵌入式设备选择 GUI 库

作者:Martin Hansen

在本文中,我们将研究两个 GUI 库, بررسی 它们之间的差异,并就何时选择每个库给出一些建议。

我所在的公司致力于帮助客户在嵌入式软件开发中做出正确的技术决策,并在之后为他们提供所选技术的支持。我的专长是嵌入式 Linux。

与客户交谈时,我发现越来越多的产品需要某种图形显示。因此,我决定是时候更多地了解嵌入式 Linux 上的 GUI 开发了。

我选择的路径是实践性的。我做了一些研究,发现最常用的库是 Qtopia(也称为 Qt embedded)和 Nano-X(以前称为 Microwindows)。

如何测试

第一个解决方案是简单地实现一些测试应用程序来演示这两个 GUI 库。这样的“测试应用程序”很少像真实的应用程序,但我这样做主要是因为我是一名工程师,工程师对底层的技术比外观更感兴趣。

“那么为什么要让工程师测试库呢?”您可能会问。好吧,将 GUI 库视为使外观成为可能的技术。因此,您需要技术视角和外观视角。

我进行此测试的另一个方面是,我没有参与任何项目,因此我带来了大多数其他程序员在开始使用库时所拥有的知识。我对实现的一些选择可能不是最佳的。它们是根据库的普通用户可获得的信息做出的——例如下面讨论的滚动图的问题。

因此,在您对我如何可以以不同的方式做事大发雷霆之前,请查看文档。文档是否清楚地说明了这个问题?如果没有,也许最好修改文档。

我决定从外部获得一些灵感,去了附近的大学,那里有一个用户中心设计 (UCD) 系。我邀请了一位学生 Esti Utami Handarini Povlsen(她是我的一位老朋友)提出一个 GUI 规范,然后我将使用这两个库来实现它。在校准了我们的技术语言以便我们能够沟通之后,我们找到了一个合适的设计,我把它带回家去实现。

我得到的设计是用于个人移动医疗设备 (PMMD) 的。该设计由一个包含一些静态按钮和一个显示文本和/或图形的可变显示区域的单个窗口组成。

事实证明,最具挑战性的部分是心跳监视器图,它是一条在屏幕上滚动的变化线。

目标设备

我用于评估的平台是 Mechatronic Brick 的基于 MIPS32 的 mb1100。mb1100 开发套件配备了 AMD Alchemy au1100 CPU、6.5 英寸 TFT 屏幕、ADS7846E 四线触摸屏、32MB RAM 和 32MB Flash。

Qtopia

我从 Qtopia 库开始。Qtopia 的创建者是挪威公司 Trolltech。Trolltech 最著名的是其桌面计算机上的 Qt 库;Qt 是 KDE 的基础 GUI。Qtopia 是 Qt 库的嵌入式版本。

Qt 和 Qtopia 都是双重许可的,在 GPL 和商业许可下。您可以从 Trolltech 网站下载 GPL 版本,并像使用任何其他 GPL 库一样使用它。这迫使您的 Qt/Qtopia 应用程序也采用 GPL。您也可以选择购买商业许可证,这允许您制作闭源应用程序。这两个版本之间的差异很小,如果有的话,当然除了许可证之外。

启动并运行

Qtopia 可以直接在 framebuffer 设备上运行,因此请确保内核已编译并支持 framebuffer,并且它正在工作。

那是容易的部分。困难的部分是使触摸屏工作。在纠正了驱动程序中的一些小故障后,我在 Qtopia 中校准设备时遇到了很多麻烦。

我将 Qtopia 与 tslib 一起用于触摸屏,在纠正驱动程序后,tslib 工作正常,并且 tslib 包中包含的小校准程序校准良好。在同一程序中使用笔绘制线条效果很好。启动 Qtopia 程序后,校准消失了,我尝试了 Qtopia 包中的校准程序,但没有成功。

我在查看 Qtopia 和 tslib 的源代码时发现了错误。当 tslib 启动时,它会查找 /etc 中的一个文件。此文件告诉 tslib 要加载哪些模块,这些模块通常包括线性化模块和不同的噪声滤波器。

线性模块是进行校准的模块。在查看 Qtopia 的源代码时,我发现程序员想要确保加载了线性模块,因此在解析 tslib 配置文件后,Qtopia 会加载线性模块,而不管配置文件中写入了什么。这意味着如果在配置文件中定义了线性模块,它将被加载两次,这会破坏功能。弄清楚这一点后,我从配置文件中删除了线性模块。(我知道正确的解决方案是更正 Qtopia 源代码,但我采取了捷径。)现在校准在 Qtopia 中工作了。

在 Qtopia 中编程

我不会详细介绍我的应用程序的实现,因为它不在本文的范围内。但是,总而言之,Qtopia 是基于 C++ 的,我认为 Qtopia 设计师对 C++ 的理念有很好的掌握。毫不奇怪,所有小部件都是对象,并且要在您自己的小部件(在您自己的类中定义)中具有标准函数(方法),您需要从基类继承。

不同的对象(小部件)需要通信。例如,如果我单击一个按钮,按钮对象可能想要告诉文本字段对象更新文本。在 Qt 以及 Qtopia 中,这是使用信号和槽完成的。它们只是带有属性的标准方法。此接口使对象之间可以相互独立。按钮只发送一个信号“clicked”,文本对象有一个槽“update”,它们编译并工作良好,彼此互不影响。然后,当我将它们放在我的应用程序中并在初始化中给出 connect (obj1, clicked, obj2, update) 命令以将信号 clicked 与槽 update 连接起来时,奇迹就发生了。当我单击按钮时,文本会更新。

这些连接甚至可以自动完成,只需给它们正确的名称即可。如果我有一个名为 cancelBtn 的小部件,带有信号 clicked,并且我创建了一个名为 on_cancelBtn_clicked 的槽,则来自 cancelBtn 的 clicked 信号会自动连接到此槽。这种信号/槽设计使代码易于阅读和维护。另一方面,如果您不熟悉信号和槽,并且您查看别人的代码,您可能会长时间盲目地寻找槽(方法)的调用。

文档

到目前为止,文档一直是一个很大的帮助。他们在编写 API 文档方面做得非常出色。但是,如果您不知道应该使用哪个 API 调用来完成任务,则 API 文档对您没有帮助。我花了很多时间使绘图对象正确工作,因为我不得不从文档的不同位置收集信息。我从未找到一种有效的方法来制作我的滚动图。我没有找到任何可以滚动我的心跳图的位图操作,所以我选择为每个滚动步骤重新绘制整个图形。可能有一种更简单的方法,但我没有找到。

因此,如果您想在 Qtopia 中进行更高级的编程,您需要找到一本好的书或指南来补充 API 文档。

Choosing a GUI Library for Your Embedded Device

图 1. 使用 Qtopia 构建的应用程序

Nano-X

Nano-X 以前被称为 Microwindows。为什么要改名?大胆猜测一下。如果您的猜测包括律师,那么您可能走对了路。

Nano-X 是 Nano-X.org 上的一个开源项目,由 Greg Haerr 发起并至今仍由他领导。Nano-X 在 MPL 许可证下获得许可。MPL 许可证允许您创建闭源驱动程序和应用程序。但是,Nano-X 源代码本身必须保持开放。如果需要,可以选择使用 GPL 许可证。

启动并运行

来自 Mechatronic Brick 的 Linux 包包括 Nano-X 库,但此版本不包括对 PNG 图片的支持。我需要 PNG 支持,所以我不得不重新编译。在我找到在 Mechatronic Brick 设置中构建时使用的配置文件后,这非常容易。我注意到 Nano-X 带有一个配置文件,该文件将 Nano-X 设置为使用 TCC(一个小型且非常快速的纯 C 编译器)构建。我决定也使用它,然后该库很快就编译完成了。

在 Nano-X 中编程

开始在 Nano-X 中编程是一个很大的变化,特别是当您来自 Qtopia 漂亮而精致的 C++ 类时。Nano-X 简单得多,这为应用程序程序员留下了更多的工作。

Nano-X 没有小部件、按钮或组合框——只有窗口。有一些库可以放在 Nano-X 之上,这将为您提供更多功能,例如 Nano-X 自己对 win32 库的重新实现以及快速轻量工具包 (FLTK)。在本文中,我们将深入研究 Nano-X 的基本部分。

基本上,在为 Nano-X 编程时,您要做四件事

  1. 创建窗口。

  2. 在窗口中绘制。

  3. 为每个窗口选择事件类型。

  4. 等待事件(事件循环)。

典型的标准应用程序窗口由一个带有框架和小 x 关闭按钮的基本窗口组成(当然,可以选择自定义此外观)。子窗口充当按钮和显示字段。是的,在 Nano-X 中,按钮被声明为选择了鼠标单击事件的子窗口。

在 Qtopia 中,我只是创建了一个类,连接了一些信号和槽,然后,奇迹发生了。在 Nano-X 中,我必须自己处理事情。Nano-X 应用程序的核心部分是事件循环,通常是一个 for (ever) 循环,其中包含 get event 函数和一个 case 结构来处理事件(有关示例,请参见列表 1)。当我收到鼠标单击事件时,我会询问哪个窗口捕获了此事件并据此采取行动。这意味着单个按钮没有隔离在自己的代码段中,而是编织到应用程序中。按钮或显示字段的基本功能当然应该在它自己的函数中,但是事件循环必须知道在按钮中选择了哪些事件以及如何处理这些事件。

列表 1. Nano-X 中的 for (ever) 循环>

for (;;) {
  GrGetNextEvent(&event);
  switch (event.type) {
    case GR_EVENT_TYPE_EXPOSURE:
      GrText(w, gc, 10, 30, text, -1, GR_TFASCII);
      break;
    case GR_EVENT_TYPE_BUTTON_DOWN:
      text="hej verden";
      GrText(w, gc, 10, 30, text, -1, GR_TFASCII);
      break;
    case GR_EVENT_TYPE_CLOSE_REQ:
      GrClose();
      exit(0);
  }
}
文档

Nano-X 的文档有点缺乏。那里有一些很棒的文档;但是,来自网页的链接没有更新,其中许多链接已失效。我使用 Google 找到了最有用的文档。也可以使用 Nano-X 源代码和邮件列表。邮件列表非常活跃,Greg Haerr 就在那里,对问题给出快速响应。

源代码中的 make doc 将使用 Doxygen 制作一些关于 API 的文档,但并非所有函数都已记录在案。我不得不直接查看源代码几次。

Choosing a GUI Library for Your Embedded Device

图 2. 使用 Nano-X 构建的应用程序

结论

在尺寸方面,Nano-X 确实遥遥领先。但是,在精致的图形和良好结构化的编程方面,Qtopia 遥遥领先。不要误会我的意思,这并非完全是 C 与 C++ 的问题。您可以使用 C 和 Nano-X 进行良好的编程,但这确实需要程序员具备更高的技能和纪律性。硬核 C 程序员经常会用 Qtopia 编写出混乱的 C++ 代码,因此 C++ 并不总是能转化为良好的实践——这一切都取决于您现有的技能、时间和学习意愿。

关于速度,我没有看到太大的差异,除了我的滚动图。使用 Qtopia,图形是抖动的,因为我没有找到实际滚动位图的方法,所以我不得不为每个步骤重新绘制整个图形。使用位图复制进行滚动,然后在 Nano-X 中很好地绘制了图形,然后仅绘制图形的新部分。如果有更多的时间和反复试验,很可能您也可以在 Qtopia 中更有效地滚动——可能通过子类化正确的对象。但是鉴于当前的文档,我没有找到方法来做到这一点。

表 1 是我制作的 PMMD 的两个版本 PMMD-QT 和 PMMD-NX 的摘要表。安装包括库的编译。代码大小取自文档。

表 1. 摘要表

 PMMD-QTPMMD-NX
GUITrolltech 的 Qtopia(GPL 版本)Nano-X
编程语言C++C
学习使用库所花费的时间大约一周(安装三天,学习 API 两天)大约一周(安装三天,学习 API 两天)
GUI 和心跳监视器图的开发时间大约两到四天大约五到七天
库的代码大小压缩后:1.1 - 3.2MB<100K
文档API:非常好;安装:需要改进API:可用;安装:需要改进
许可证GPL 许可证和商业许可证。GPL 版本可免费下载;商业版本必须购买。MPL 许可证,可以用于闭源驱动程序和应用程序。Nano-X 可免费下载。

小部件

图形用户界面 (GUI) 中的小部件是 GUI 的单个组件的概念,例如按钮、时钟或文本输入字段。

维基百科关于小部件: en.wikipedia.org/wiki/Widget_%28computing%29

Linux Framebuffer

Linux framebuffer (fbdev, en.wikipedia.org/wiki/Linux_framebuffer) 是一个图形硬件无关的抽象层,用于在控制台上显示图形,而无需依赖于特定于系统的库,例如 SVGALib 或 X Window System。

Linux framebuffer 设备继承自旧的显示硬件 (en.wikipedia.org/wiki/Framebuffer),其中要显示的图片由硬件从内存区域中拉取。

本文资源: /article/9460

Martin Hansen 在丹麦公司 Center for Software Innovation (CSI, www.cfsi.dk) 工作。CSI 通过咨询和提供“技术注入”为公司提供嵌入式开发方面的知识。Martin 是公司嵌入式 Linux 方面的专家。他使用 Linux 已超过十年,并且在过去的两年中一直从事嵌入式 Linux 工作。他拥有电子学方面的实践教育和计算机科学学士学位。

加载 Disqus 评论