图形用户界面GUI(Graphics User Interface)是迄今为止计算机系统中最为成熟的人机交互技术。一个好的图形用户界面的设计不仅要考虑到具体硬件环境的限制,而且还要考虑到用户的喜好等。
由于图形用户界面的引入主要是从用户角度出发的,因此用户自身的主观感受对图形用户界面的评价占了很大比重,比如,易用性、直观性、友好性,等等。另外,从纯技术的角度看,仍然也会有一些标准需要考虑,比如,跨平台性、对硬件的要求等。在嵌入式系统开发和应用中,我们所考虑的问题主要集中在图形用户界面对硬件的要求,以及对硬件类型的敏感性方面,在提供给用户的最终界面方面只是要求简单实用就够了。
虽然不同的GUI系统因为其使用场合或服务目的不同,具体实现互有差异,但是总结起来,一般在逻辑上可以分为以下几个模块:底层I/O设备驱动(显示设备驱动、鼠标驱动、键盘驱动等)、基本图形引擎(画点、画线、区域填充)、消息驱动机制、高层图形引擎(画窗口、画按钮),以及GUI应用程序接口(API)。
底层I/O设备驱动,例如,显示驱动、鼠标驱动、键盘驱动等构成了GUI的硬件基础。由于此类设备的多样性,需要对其进行抽象,并提供给上层一个统一的调用接口;而各类设备驱动则自成一体,形成一个GUI设备管理模块。当然,从操作系统内核的角度看,GUI设备管理模块则是操作系统内核的I/O设备管理的一部分。
基本图形引擎模块完成一些基本的图形操作,如画点、画线、区域填充等。它直接和底层I/O设备打交道,同时,多线程或者多进程机制的引入也为基本图形模块的实现提供了很大的灵活性。
消息不仅是底层I/O硬件和GUI上层进行交互的基础,同时也是各类GUI组件如窗口、按钮等相互作用的重要途径。一个GUI系统的消息驱动机制的效率对该系统的性能,尤其是对响应速度等性能的影响很大。
高级图形引擎模块则在消息传递机制和基本图形引擎的基础上完成对诸如窗口、按钮等的管理。
GUI API则是提供给最终程序员的编程接口,使得他们能够利用GUI体系所提供的GUI高级功能快速开发GUI应用程序。
另外,为了实现GUI系统,一般需要用到操作系统内核提供的功能,如线程机制、进程管理。当然,不可避免地需要用到内存管理、I/O设备管理,甚至还可能有文件管理。
从用户的观点来看,图形用户界面(GUI)是系统的一个至关重要的方面:由于用户通过GUI与系统进行交互,所以GUI应该易于使用并且非常可靠。此外,它不能占用太多的内存,以便在内存受限的微型嵌入式设备上无缝执行。由此可见,它应该是轻量级的,并且能够快速装入。
嵌入式GUI要求简单、直观、可靠、占用资源小且反应快速,以适应系统硬件资源有限的条件。另外,由于嵌入式系统硬件本身的特殊性,嵌入式GUI应具备高度可移植性与可裁减性,以适应不同的硬件条件和使用需求。总体来讲,嵌入式GUI具备以下特点:
一个能够移植到多种硬件平台上的嵌入式GUI系统,应至少抽象出两类设备:基于图形显示设备(如VGA卡)的图形抽象层GAL(Graphic Abstract Layer)和基于输入设备(如键盘,触摸层等)的输入抽象层IAL(Input Abstract Layer)。GAL层完成系统对具体的显示硬件设备的操作,最大限度地隐藏各种不同硬件的技术实现细节,为程序开发人员提供统一的图形编程接口。IAL层则需要实现对于各类不同输入设备的控制操作,提供统一的调用接口,如下图所示。GAL层与IAL层设计概念的引入,可以显著提高嵌入式GUI的可移植性。
X Window是一种以位图方式显示的软件窗口系统,最初是1984年麻省理工学院的研究成果,之后变成UNIX、类UNIX、以及OpenVMS等操作系统所一致适用的标准化软件工具包及显示架构的运作协议。X Window通过软件工具及架构协议来建立操作系统所用的图形用户界面,此后则逐渐扩展适用到各形各色的其他操作系统上,几乎所有的操作系统都能支持与使用X Window,GNOME和KDE也都是以X Window为基础。
FrameBuffer是出现在2.2.xx内核中的一种驱动程序接口。由于Linux工作在保护模式,所以用户态进程无法像DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。在使用Framebuffer时,Linux将显卡置于图形模式。Framebuffer就是模仿显卡的功能,相当于抽象的显卡硬件结构,实现了通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反映在屏幕上。这种操作是抽象的、统一的。用户不必关心物理显存的位置、换页机制等具体细节,因为这些都是由Framebuffer设备驱动来完成的。
SVGALib是Linux系统中最早出现的非X图形支持库,是Linux下的VGA驱动函数库。虽然它的品质有点低,支援显卡种类也不多,但是有许多的游戏及程序都是用它来做开发,可以算是非官方的标准了。这个库从最初对标准VGA兼容芯片的支持开始,已经发展到对老式SVGA芯片,以及现今流行的高级视频芯片的支持。它为用户提供了在控制台上进行图形编程的接口,使用户可以在PC兼容系统上方便地获得图形支持。
SVGALib作为一个老的图形支持库,目前的应用范围越来越小,尤其在Linux内核增加了FrameBuffer驱动支持之后,有逐渐被其他图形库替代的趋势。
LibGGI是一个跨平台的绘图库,可以建立一个一般性的图形接口,这个抽象接口连同相关的输入(鼠标、键盘、游戏杆等)接口一起,可以方便地运行在X Window、SVGALib、FrameBuffer等之上。建立在LibGGI之上的应用程序,不经重新编译,就可以在上述这些底层图形接口上运行。
在Linux上,LibGGI是通过调用FrameBuffer或SVGALib来完成图形操作的,可能速度比较慢。但在某些不支持FrameBuffer或vga的系统上,采用LibGGI仍然是一种不错的选择。
Xlib及其他相关函数库.X Window系统中进行图形编程时,可以选择直接使用Xlib。Xlib实际上是对底层 X 协议的封装,可通过该函数库进行一般的图形输出。如果用户的X Server支持DGA,则可以通过DGA扩展直接访问显示设备,从而获得加速支持。
SDL是一个跨平台的多媒体游戏支持库。其中包含了对图形、声音、游戏杆、线程等的支持,目前可以运行在许多平台上。SDL支持图形的功能强大,高级图形处理能力尤为突出,可以实现Alpha混合、透明处理、YUV覆盖、Gamma校正等。在SDL环境中能够非常方便地加载支持OpenGL的Mesa库,从而提供对二维和三维图形的支持。
Allegro是一个专门为x86平台设计的游戏图形库。最初的Allegro运行在DOS环境下,目前也可运行在Linux FrameBuffe控制台、Linux SVGALib、X Window等系统上。Allegro提供了丰富的图形功能,包括矩形填充和样条曲线生成等,而且具有较好的三维图形显示能力。由于Allegro的许多关键代码是采用汇编编写的,所以该函数库具有运行速度快、占用资源少的特点。
Mesa3D是一个兼容OpenGL规范的开放源码函数库,是目前Linux上提供专业三维图形支持的惟一选择。Mesa3D也是一个跨平台的函数库,能够运行在X Window、X Window with DGA、BeOS、Linux SVGALib等平台上。
DirectFB是特别为Linux FrameBuffer加速的一个图形库,正在尝试建立一个兼容GTK(GIMP Toolkit)的嵌入式GUI系统。
Microwindows是Century Software的开放源代码项目,设计用于带小型显示单元的微型设备。它有许多针对现代图形视窗环境的功能部件,可被多种平台支持。
Microwindows体系结构是基于客户机/服务器(Client/Server)分层设计的。最底层是屏幕和输入设备,通过驱动程序来与实际硬件交互;中间层提供底层硬件的抽象接口,进行窗口管理;最上层支持两种API:第一种支持Win32/WinCE API,称为Microwindows,另一种支持的API与GDK(GTK+ Drawing Kit)非常相似,用在Linux上称为Nano-X,用于占用资源少的应用程序。
Microwindows支持1、2、4和8bpp(每像素的位数)的灰度显示,以及8、16、24和32 bpp的真彩色显示。Microwindows提供了相对完善的图形功能和一些高级的特性,如Alpha混合、三维支持和TrueType字体支持等。该系统为了提高运行速度,也改进了基于Socket套接字的X实现模式,采用基于消息机制的Server/Client传输机制。Microwindows还支持速度更快的帧缓冲区。
Nano-X服务器占用的存储器资源大约在100KB~150KB。原始Nano-X应用程序的平均大小在30KB~60KB。与Xlib的实现不同,Nano-X仍在每个客户机上同步运行,这意味着一旦发送了客户机请求包,服务器在为另一个客户机提供服务之前一直等待,直到整个包都到达为止。这使服务器代码非常简单,而运行的速度仍非常快。
MiniGUI是自由软件项目(遵循LGPL条款发布),其目标是为基于Linux的实时嵌入式系统提供一个轻量级的图形用户界面支持系统。MiniGUI为实时嵌入式操作系统提供了完善的图形及图形用户界面支持。可移植性设计使得它不论在哪个硬件平台、哪种操作系统上运行,均能为上层应用程序提供一致的应用程序编程接口(API)。 在MiniGUI几年的发展过程中,有许多值得一提的技术创新点包括:
图形抽象层。图形抽象层对顶层API基本没有影响,但大大方便了MiniGUI应用程序的移植、调试等工作。目前包含三个图形引擎,SVGALib、LibGGI,以及直接基于Linux FrameBuffer的Native Engine。
多字体和多字符集支持。这部分通过设备上下文(DC)的逻辑字体(LOGFONT)实现,不管是字体类型还是字符集,都可以非常方便地进行扩充。应用程序在启动时,可切换系统字符集,比如GB、BIG5、EUCKR、UJIS。MiniGUI的这种字符集支持,这种实现更适合于嵌入式系统。
两个不同架构的版本。最初的MiniGUI运行在PThread库之上,这个版本适合于功能单一的嵌入式系统,但存在系统健壮性不够的缺点。在0.9.98版本中,引入了MiniGUI-Lite版本,这个版本在提高系统健壮性的同时,通过一系列创新途径,避免了传统C/S结构的弱点,为功能复杂的嵌入式系统提供了一个高效、稳定的GUI系统。
在MiniGUI 1.1.0版本的开发中,参照SDL和Allegro的图形部分,重新设计了图形抽象层,增强了图形功能,同时增强了MiniGUI-Lite版本的某些特性。增强的MiniGUI-Lite支持层的设计,同一层可以容纳多个同时显示的客户程序,并平铺在屏幕上显示。新的GAL支持硬件加速能力,并能够充分使用显示内存;新GAL之上的新GDI接口得到进一步增强,可以支持Alpha混合、透明位块传输、光栅操作、YUV覆盖、Gamma校正,以及高级图形功能(椭圆、多边形、样条曲线),等等。
OpenGUI在Linux系统上已经应用很长时间了。OpenGUI基于一个用汇编实现的x86图形内核,提供了一个快速、32位、高层的C/C++图形接口。OpenGUI也是一个公开源码(LGPL)项目,最初的名字叫FastGL,只支持256色的线性显存模式。目前,OpenGUI也支持其他显示模式,并且支持多种操作系统平台,比如,MS-DOS、QNX和Linux,等等,不过目前只支持x86硬件平台。
OpenGUI也分为三层:最低层是由汇编编写的快速图形引擎;中间层提供了图形绘制API,包括线条、矩形、圆弧等,并且兼容于Borland的BGI API;第三层用C++编写,提供了完整的GUI对象集。OpenGUI提供了消息驱动的API和BMP文件格式支持,OpenGUI比较适合基于x86平台的实时系统,可移植性稍差,目前的发展也基本停滞。
Qt/Embedded(简称QtE)是一个专门为嵌入式系统设计的图形用户界面的工具包,由挪威Trolltech(奇趣)公司开发,最初作为跨平台的开发工具用于Linux台式机。它支持各种有UNIX 和Microsoft Windows特点的系统平台。
Qt/Embedded以原始Qt为基础,是Qt的嵌入式版本,许多基于Qt的X Window程序可以非常方便地移植到Qt/Embedded上,因此,自从Qt/Embedded以GPL条款形式发布以来,就有大量的嵌入式Linux开发商转到了Qt/Embedded系统上,比如,韩国的Mizi公司。
Qt/Embedded通过Qt API与Linux I/O设备直接交互,是面向对象编程的理想环境。面向对象的体系结构使代码结构化、可重用并且运行快速,与其他GUI相比,Qt GUI非常快,没有分层,这使得Qt/Embedded成为基于Qt的程序的最紧凑环境。
Qt/Embedded延续了Qt在X上的强大功能,在底层摒弃了X lib,仅采用FrameBuffer作为底层图形接口。同时,将外部输入设备抽象为keyboard和mouse输入事件,底层接口支持键盘、GPM鼠标、触摸屏,以及用户自定义的设备等。 Qt/Embedded类库完全采用C++封装,丰富的控件资源和较好的可移植性是Qt/Embedded最为突出的优点。它的类库接口完全兼容于同版本的Qt-X11,使用X下的开发工具可以直接开发基于Qt/Embedded的应用程序GUI。
Qt/Embedded与QT/X11比较
总的来说,QtE拥有下面一些特征:
交叉编译器:arm-linux-gcc-4.3.3
源码包:
源码包存放路径:/usr/local/qt
qt-x11版本可以产生Qt开发工具,如designer、qvfb,嵌入式的开发有了qvfb,就可以不需 要实际的开发板,也可以开发Qt应程序。 进入解压目录,作如下执行:
# ./configure
# make
# cd tools/qvfb
# make
# cd ../ ..
# make install
一般不会出现错误,完了之后复制qvfb相关文件到安装目录,命令如下:
# cp tools/qvfb/qvfb* /usr/local/Trolltech/Qt-4.5.3/bin
至此,qt-x11环境搭建好,时间比较长。安装好的路径默认为:/usr/local/Trolltech/Qt-4.5.3
进入解压目录,执行如下命令:
# ./autogen.sh# CC=arm-linux-gcc
# echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache
# ./configure --prefix=/opt/ts/ --host=arm-linux --cache-file=arm-linux.cache
# make
# make install
进入解压目录,执行如下命令:
# ./configure -xplatform qws/linux-arm-g++ -embedded arm -shared -little-endian -qt-kbd-usb -qt-mouse-tslib -qt-libpng -qt-libjpeg -qt-gif -depths 4,8,16,32 -confirm-license -prefix /usr/local/Trolltech/QtEmbedded-4.5.3-arm/ -I/opt/ts/include -L/opt/ts/lib# make
# make install
# make clean
qt-embedded安装在 /usr/local/Trolltech/QtEmbedded-4.5.3-arm下。
在qt-x11安装的目录下新建setenv.sh脚本文件。
# gedit setenv.sh
添加如下内容:
PATH=/usr/local/Trolltech/Qt-4.5.3/bin:$PATH
LD_LIBRARY_PATH=/usr/local/Trolltech/Qt-4.5.3/lib:$LD_LIBRARY_PATH
在qt-embedded目录下新建setenv-arm.sh。
# gedit setenv-arm.sh
添加如下内容:
QTEDIR=/usr/local/Trolltech/QtEmbedded-4.5.3-arm
PATH=/usr/local/Trolltech/QtEmbedded-4.5.3-arm/bin:$PATH
LD_LIBRARY_PATH=/usr/local/Trolltech/QtEmbedded-4.5.3-arm/lib:$LD_LIBRARY_PATH
当我们要使用相应版本的Qt时,先运行对应脚本。比如要用到qt-x11,在Qt-4.5.3中执行:
# . setenv.sh 或者source setenv.sh
之后就可以直接在终端输入qmake之类了。
1:首先在目标板中建立跟ubuntu中一样的目录路径(tslib安装的路径和QtEmbedded-4.5.3-arm的路径)。
2:将交叉编译好的tslib文件cp到开发板中,include和lib下的pkgconfig不需拷贝。
3:拷贝字库文件,把PC上/usr/local/Trolltech/QtEmbedded-4.5.3-arm/lib/fonts目录下的字库拷贝到开发板的对应的目录下(只需一种就可以)。
4:拷贝运行库:在PC 机上复制下列文件到开发板的/usr/local/Trolltech/QtEmbedded-4.5.3-arm/lib目录下:libQtGui.so.4,libQtCore.so.4,libQtNetwork.so.4
export TSLIB_TSDEVICE=/dev/input/event0
export TSLIB_CONFFILE=/home/coco/tslib/etc/ts.conf
export TSLIB_PLUGINDIR=/home/coco/tslib/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export QWS_MOUSE_PROTO=tslib:/dev/input/event0
export QWS_DISPLAY=LinuxFb:/dev/fb0
export QWS_SIZE=320x240
export QTDIR=/usr/local/Trolltech/QtEmbedded-4.5.3-arm
export QPEDIR=/usr/local/Trolltech/QtEmbedded-4.5.3-arm
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:/home/coco/tslib/lib:/lib:$LD_LIBRARY_PATH
进入pc端/usr/local/Trolltech/QtEmbedded-4.5.3-arm/examples/widgets/digitalclock目录,执行如 下:
# qmake -o Makefile digitalcloc.pro
# make
然后将digitalclock拷贝至开发板/usr/local/Trolltech/QtEmbedded-4.5.3-arm/bin目录中。发布能在arm板上运行的程序一般步骤如下:
1:进入qt-x11,借助qmake可将.pro工程文件生成Makefile;方法如下 qmake -o Makefile xx.pro 。 之后执行make会生成x86平台上可运行的程序。在qvfb下可以查看该程序运行状态。
2:进入qt-embedded,同样借助qmake,也可生成Makefiel,然后make之后,交叉编译生成的就是arm平台可运行的程序。所以从这里可以看出,qt-x11不但提供了qvfb,designer等工具,更主要的是在发布程序之前进行测试与调试,完美之后,再用交叉编译工具将整个工程生成arm平台可执行的程序。
1:测试触摸屏程序,进入\home\coco\tslib\bin ,执行:
# ./ts_calibrate
进行屏幕校正。
# ./ digitalclock
1 #include
2 #include
3 int main(int argc, char *argv[])
4 {
5 QApplication app(argc, argv);
6 QLabel *label = new QLabel("Hello Qt!");
7 label->show();
8 return app.exec();
9 }
第二个例子演示如何让程序响应用户的动作,这个程序由一个按钮组成,当用户 点击它时会退出程序。程序代码和Hello相似,只不过这里会用QpushButton代替 Qlabel作为主控件,并且我们将会把用户的动作和一些程序联系起来。
1 #include
2 #include
3 int main(int argc, char *argv[])
4 {
5 QApplication app(argc, argv);
6 QPushButton *button = new QPushButton("Quit");
7 QObject::connect(button, SIGNAL(clicked()),&app, SLOT(quit()));
8 button->show();
9 return app.exec();
10 }
QT控件会通过发射一个信号(signal)来表明用户做个一个操作或控件的状态发生 了改变。例如:当用户点击按钮时,QpushButton就会发射clicked()信号。信号可 以连接到一个函数上(一般叫做槽(slot)(国内一般称之为信号槽)),当信号发射后, 与之相两的信号槽就会被执行。例如:我们将按钮的clicked()信号和Qapplication 对象的quit()信号槽连接起来。
嵌入式软件开发通常都采用交叉编译的方式进行,基于Qt/Embedded的GUI应用开发也采用这样的模式。先在宿主机上调试应用程序,调试通过后,经过交叉编译移植到目标板上。
Qt/Embedded直接写入帧缓存,在宿主机上则是通过qvfb(virtual framebuffer)来模拟帧缓存。qvfb是X窗口用来运行和测试应用程序的系统程序。qvfb使用了共享内存存储区域(虚拟的帧缓存)来模拟帧缓存并且在一个窗口中模拟一个应用程序来显示帧缓存,显示的区域被周期性的改变和更新。