第12章Qt图形编程基础.docx
- 文档编号:17664049
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:44
- 大小:678.78KB
第12章Qt图形编程基础.docx
《第12章Qt图形编程基础.docx》由会员分享,可在线阅读,更多相关《第12章Qt图形编程基础.docx(44页珍藏版)》请在冰点文库上搜索。
第12章Qt图形编程基础
第12章Qt图形编程基础
本章目标
掌握嵌入式GUI的种类和特点
掌握Qt中的信号与槽的机制
掌握Qt/Embedded的安装和配置
掌握Qt/Embedded应用程序的基本流程
12.1嵌入式GUI简介
目前的桌面机操作系统大多有着美观、操作方便、功能齐全的GUI(图形用户界面),例如KDE或者GNOME。
GUI(图形用户界面)是指计算机与其使用者之间的对话接口,可以说,GUI是当今计算机技术的重大成就。
它的存在为使用者提供了友好便利的界面,并大大地方便了非专业用户的使用,使得人们从繁琐的命令中解脱出来,可以通过窗口、菜单方便地进行操作。
而在嵌入式系统中,GUI的地位也越来越重要,但是不同于桌面机系统,嵌入式GUI要求简单、直观、可靠、占用资源小且反应快速,以适应系统硬件资源有限的条件。
另外,由于嵌入式系统硬件本身的特殊性,嵌入式GUI应具备高度可移植性与可裁减性,以适应不同的硬件条件和使用需求。
总体来讲,嵌入式GUI具备以下特点:
体积小;
运行时耗用系统资源小;
上层接口与硬件无关,高度可移植;
高可靠性;
在某些应用场合应具备实时性。
UNIX环境下的图形视窗标准为XWindowSystem,Linux是类UNIX系统,所以顶层运行的GUI系统是兼容X标准的XFree86系统。
X标准大致可以划分XServer、GraphicLibrary(底层绘图函数库)、Toolkits、WindowManager等几大部分。
其好处是具有可扩展性、可移植性等优点,但对于嵌入式系统而言无疑太过庞大、累赘、低效。
目前流行的嵌入式GUI与X思路不同,这些GUI一般不局限于X标准,更强调系统的空间和效率。
12.1.1Qt/Embedded
表12.1归纳了Qt/Embedded的一些优缺点。
表12.1Qt/Embedded分析
Qt/Embedded分析
优点
以开发包形式提供
包括了图形设计器、Makefile制作工具、字体国际化工具、Qt的C++类库等
跨平台
支持MicrosoftWindows95/98/2000、MicrosoftWindowsNT、MacOSX、Linux、Solaris、HP-UX、Tru64(DigitalUNIX)、Irix、FreeBSD、BSD/OS、SCO、AIX等众多平台
类库支持跨平台
Qt类库封装了适应不同操作系统的访问细节,这正是Qt的魅力所在
模块化
可以任意裁减
缺点
结构也过于复杂臃肿,很难进行底层的扩充、定制和移植
例如:
尽管Qt/Embedded声称,它最小可以裁剪到几百KB,但这时的Qt/Embedded库已经基本失去了使用价值
它提供的控件集沿用了PC风格,并不太适合许多手持设备的操作要求
Qt/Embedded的底层图形引擎只能采用framebuffer,只是针对高端嵌入式图形领域的应用而设计的
由于该库的代码追求面面俱到,以增加它对多种硬件设备的支持,造成了其底层代码比较凌乱,各种补丁较多的问题
12.1.2MiniGUI
提起国内的开源软件,就肯定会提到MiniGUI,它由魏永明先生和众多志愿者开发,是一个基于Linux的实时嵌入式系统的轻量级图形用户界面支持系统。
MiniGUI分为最底层的GAL层和IAL层,向上为基于标准POSIX接口中pthread库的Mini-thread架构和基于Server/Client的Mini-Lite架构。
其中前者受限于thread模式对于整个系统的可靠性——进程中某个thread的意外错误可能导致整个进程的崩溃,该架构应用于系统功能较为单一的场合。
Mini-Lite应用于多进程的应用场合,采用多进程运行方式设计的Server/Client架构能够较好地解决各个进程之间的窗口管理、Z序剪切等问题。
MiniGUI还有一种从Mini-Lite衍生出的standalone运行模式。
与Lite架构不同的是,standalone模式一次只能以窗口最大化的方式显示一个窗口。
这在显示屏尺寸较小的应用场合具有一定的应用意义。
MiniGUI的IAL层技术SVGAlib、LibGGI、基于framebuffer的native图形引擎以及哑图形引擎等,对于Trolltech公司的QVFB在XWindow下也有较好的支持。
IAL层则支持Linux标准控制台下的GPM鼠标服务、触摸屏、标准键盘等。
MiniGUI下丰富的控件资源也是MiniGUI的特点之一。
当前MiniGUI的最新版本是1.3.3。
在该版本的控件中已经添加了窗口皮肤、工具条等桌面GUI中的高级控件支持。
对比其他系统,“Mini”是MiniGUI的特色,轻量、高性能和高效率的MiniGUI已经应用在电视机顶盒、实时控制系统、掌上电脑等诸多场合。
12.1.3Microwindows、TinyX等
MicrowindowsOpenSourceProject成立的宗旨在于针对体积小的装置,建立一套先进的视窗环境,在Linux桌面上通过交叉编译可以很容易地制作出Microwindows的程序。
Microwindows能够在没有任何操作系统或其他图形系统的支持下运行,它能对裸显示设备进行直接操作。
这样,Microwindows就显得十分小巧,便于移植到各种硬件和软件系统上。
然而Microwindows的免费版本进展一直很慢,几乎处于停顿状态,而且至今为止,国内没有任何一家对Microwindows提供全面技术支持、服务和担保的专业公司。
TinyXServer是XFree86Project的一部分,由KeithPachard发展起来的,而他本身就是XFree86专案的核心成员之一。
一般的XServer都过于庞大,因此KeithPackard就以XFree86为基础,精简而成TinyXServer,它的体积可以小到几百KB,非常适合应用于嵌入式环境。
就纯XWindowSystem搭配TinyXServer架构来说,其最大的优点就是具有很好的弹性开发机制,并能大大提高开发速度。
因为与桌面的X架构相同,因此相对于很多以Qt、GTK+、FLTK等为基础开发的软件可以很容易地移植过来。
虽然移植方便,但是却有体积大的缺点,由于很多软件本来是针对桌面环境开发的,因此无形之中具备了桌面环境中很多复杂的功能。
因此“调校”变成采用此架构最大的课题,有时候重新改写可能比调校所需的时间还短。
表12.2总结了常见GUI的参数比较。
表12.2常见GUI参数比较
名称
参数
MiniGUI
OpenGUI
Qt/Embedded
API(完备性)
Win32(很完备)
私有(很完备)
Qt(C++)(很完备)
函数库的典型大小
300KB
300KB
600KB
移植性
很好
只支持x86平台
较好
授权条款
LGPL
LGPL
QPL/GPL
系统消耗
小
最小
最大
操作系统支持
Linux
Linux,DOS,QNX
Linux
12.2Qt/Embedded开发入门
12.2.1Qt/Embedded介绍
1.架构
图12.1Qt/Embedded与Qt/
X11的Linux版本的比较
Qt/Embedded以原始Qt为基础,并做了许多出色的调整以适用于嵌入式环境。
Qt/Embedded通过QtAPI与LinuxI/O设施直接交互,成为嵌入式Linux端口。
同Qt/X11相比,Qt/Embedded很省内存,因为它不需要一个X服务器或是Xlib库,它在底层抛弃了Xlib,采用framebuffer(帧缓冲)作为底层图形接口。
同时,将外部输入设备抽象为keyboard和mouse输入事件。
Qt/Embedde的应用程序可以直接写内核缓冲帧,这避免开发者使用繁琐的Xlib/Server系统。
图12.1所示比较了Qt/Embedded与Qt/X11的架构区别。
使用单一的API进行跨平台的编程可以有很多好处。
提供嵌入式设备和桌面计算机环境下应用的公司可以培训开发人员使用同一套工具开发包,这有利于开发人员之间共享开发经验与知识,也使得管理人员在分配开发人员到项目中的时候增加灵活性。
更进一步来说,针对某个平台而开发的应用和组件也可以销售到Qt支持的其他平台上,从而以低廉的成本扩大产品的市场。
(1)窗口系统。
一个Qt/Embedded窗口系统包含了一个或多个进程,其中的一个进程可作为服务器。
该服务进程会分配客户显示区域,以及产生鼠标和键盘事件。
该服务进程还能够提供输入方法和一个用户接口给运行起来的客户应用程序。
该服务进程其实就是一个有某些额外权限的客户进程。
任何程序都可以在命令行上加上“-qws”的选项来把它作为一个服务器运行。
客户与服务器之间的通信使用共享内存的方法实现,通信量应该保持最小,例如客户进程直接访问帧缓冲来完成全部的绘制操作,而不会通过服务器,客户程序需要负责绘制它们自己的标题栏和其他式样。
这就是Qt/Embedded库内部层次分明的处理过程。
客户可以使用QCOP通道交换消息。
服务进程简单的广播QCOP消息给所有监听指定通道的应用进程,接着应用进程可以把一个插槽连接到一个负责接收的信号上,从而对消息做出响应。
消息的传递通常伴随着二进制数据的传输,这是通过一个QDataStream类的序列化过程来实现的,有关这个类的描述,请读者参考相关资料。
QProcess类提供了另外一种异步的进程间通信机制。
它用于启动一个外部的程序并且通过写一个标准的输入和读取外部程序的标准输出和错误码来和它们通信。
(2)字体
Qt/Embedded支持4种不同的字体格式:
TrueType字体(TTF),PostscriptType1字体,位图发布字体(BDF)和Qt的预呈现(Pre-rendered)字体(QPF)。
Qt还可以通过增加Qfont-
Factory的子类来支持其他字体,也可以支持以插件方式出现的反别名字体。
每个TTF或者TYPE1类型的字体首次在图形或者文本方式的环境下被使用时,这些字体的字形都会以指定的大小被预先呈现出来,呈现的结果会被缓冲。
根据给定的字体尺寸(例如10或12点阵)预先呈现TTF或者TYPE1类型的字体文件并把结果以QPF的格式保存起来,这样可以节省内存和CPU的处理时间。
QPF文件包含了一些必要的字体,这些字体可以通过makeqpf工具取得,或者通过运行程序时加上“-savefonts”选项获取。
如果应用程序中使用到的字体都是QPF格式,那么Qt/Embedded将被重新配置,并排除对TTF和TYPE1类型的字体的编译,这样就可以减少Qt/Embedded的库的大小和存储字体的空间。
例如一个10点阵大小的包含所有ASCII字符的QPF字体文件的大小为1300字节,这个文件可以直接从物理存储格式映射成为内存存储格式。
Qt/Embedded的字体通常包括Unicode字体的一部分子集,ASCII和Latin-1。
一个完整的16点阵的Unicode字体的存储空间通常超过1MB,我们应尽可能存储一个字体的子集,而不是存储所有的字,例如在一个应用中,仅仅需要以Cappuccino字体、粗体的方式显示产品的名称,但是却有一个包含了全部字形的字体文件。
(3)输入设备及输入法。
Qt/Embedded3.0支持几种鼠标协议:
BusMouse、IntelliMouse,Microsoft和MouseMan.Qt/
Embedded还支持NECVr41XX和iPAQ的触摸屏。
通过从QWSMouseHandler或者Qcalibra-
tedMouseHandler派生子类,开发人员可以让Qt/Embedded支持更多的客户指示设备。
Qt/Embedded支持标准的101键盘和Vr41XX按键,通过子类化QWSKeyboardHandler可以让Qt/Embedded支持更多的客户键盘和其他的非指示设备。
对于非拉丁语系字符(例如阿拉伯、中文、希伯来和日语)的输入法,需要把它写成过滤器的方式,并改变键盘的输入。
输入法的作者应该对全部的QtAPI的使用有完整的认识。
在一个无键盘的设备上,输入法成了惟一的输入字符的手段。
Qtopia提供了4种输入方法:
笔迹识别器、图形化的标准键盘、Unicode键盘和基于字典方式提取的键盘。
(4)屏幕加速
通过子类化QScreen和QgfxRaster可以实现硬件加速,从而为屏幕操作带来好处。
Troll-
tech提供了Mach64和Voodoo3视频卡的硬件加速的驱动例子,同时可以按照协议编写其他的驱动程序。
2.Qt的开发环境
Qt/Embedded的开发环境可以取代那些我们熟知的UNIX和Windows开发工具。
它提供了几个跨平台的工具使得开发变得迅速和方便,尤其是它的图形设计器。
UNIX下的开发者可以在PC机或者工作站使用虚拟缓冲帧,从而可以模仿一个和嵌入式设备的显示终端大小,像素相同的显示环境。
嵌入式设备的应用可以在安装了一个跨平台开发工具链的不同的平台上编译。
最通常的做法是在一个UNIX系统上安装跨平台的带有libc库的GNUC++编译器和二进制工具。
在开发的许多阶段,一个可替代的做法是使用Qt的桌面版本,例如通过Qt/X11或是Qt/Windows来进行开发。
这样开发人员就可以使用他们熟悉的开发环境,例如微软公司的VisualC++或者BorlandC++。
在UNIX操作系统下,许多环境也是可用的,例如Kdevelop,它也支持交互式开发。
如果Qt/Embedded的应用是在UNIX平台下开发的话,那么它就可以在开发的机器上以一个独立的控制台或者虚拟缓冲帧的方式来运行,对于后者来说,其实是有一个X11的应用程序虚拟了一个缓冲帧。
通过指定显示设备的宽度、高度和颜色深度,虚拟出来的缓冲帧将和物理的显示设备在每个像素上保持一致。
这样每次调试应用时开发人员就不用总是刷新嵌入式设备的Flash存储空间,从而加速了应用的编译、链接和运行周期。
运行Qt的虚拟缓冲帧工具的方法是在Linux的图形模式下运行以下命令:
qvfb(回车)
当Qt嵌入式的应用程序要把显示结果输出到虚拟缓冲帧时,我们在命令行运行这个程序,并在程序名后加上-qws的选项。
例如:
$>hello–qws。
3.Qt的支撑工具
Qt包含了许多支持嵌入式系统开发的工具,有两个最实用的工具是qmake和Qtdesigner(图形设计器)。
qmake是一个为编译Qt/Embedded库和应用而提供的Makefile生成器。
它能够根据一个工程文件(.pro)产生不同平台下的Makefile文件。
qmake支持跨平台开发和影子生成,影子生成是指当工程的源代码共享给网络上的多台机器时,每台机器编译链接这个工程的代码将在不同的子路径下完成,这样就不会覆盖别人的编译链接生成的文件。
qmake还易于在不同的配置之间切换。
Qt图形设计器可以使开发者可视化地设计对话框而不需编写代码。
使用Qt图形设计器的布局管理可以生成能平滑改变尺寸的对话框。
qmake和Qt图形设计器是完全集成在一起的。
12.2.2Qt/Embedded信号和插槽机制
1.机制概述
信号和插槽机制是Qt的核心机制,要精通Qt编程就必须对信号和插槽有所了解。
信号和插槽是一种高级接口,应用于对象之间的通信,它是Qt的核心特性,也是Qt区别于其他工具包的重要地方。
信号和插槽是Qt自行定义的一种通信机制,它独立于标准的C/C++语言,因此要正确地处理信号和插槽,必须借助一个称为moc(MetaObjectCompiler)的Qt工具,该工具是一个C++预处理程序,它为高层次的事件处理自动生成所需要的附加代码。
所谓图形用户接口的应用就是要对用户的动作做出响应。
例如,当用户单击了一个菜单项或是工具栏的按钮时,应用程序会执行某些代码。
大部分情况下,是希望不同类型的对象之间能够进行通信。
程序员必须把事件和相关代码联系起来,这样才能对事件做出响应。
以前的工具开发包使用的事件响应机制是易崩溃的,不够健壮的,同时也不是面向对象的。
以前,当使用回调函数机制把某段响应代码和一个按钮的动作相关联时,通常把那段响应代码写成一个函数,然后把这个函数的地址指针传给按钮,当那个按钮被单击时,这个函数就会被执行。
对于这种方式,以前的开发包不能够确保回调函数被执行时所传递进来的函数参数就是正确的类型,因此容易造成进程崩溃。
另外一个问题是,回调这种方式紧紧地绑定了图形用户接口的功能元素,因而很难进行独立的开发。
信号与插槽机制是不同的。
它是一种强有力的对象间通信机制,完全可以取代原始的回调和消息映射机制。
在Qt中信号和插槽取代了上述这些凌乱的函数指针,使得用户编写这些通信程序更为简洁明了。
信号和插槽能携带任意数量和任意类型的参数,它们是类型完全安全的,因此不会像回调函数那样产生coredumps。
图12.2对象间信号与插槽的关系
所有从QObject或其子类(例如Qwidget)派生的类都能够包含信号和插槽。
当对象改变状态时,信号就由该对象发射(emit)出去了,这就是对象所要做的全部工作,它不知道另一端是谁在接收这个信号。
这就是真正的信息封装,它确保对象被当作一个真正的软件组件来使用。
插槽用于接收信号,但它们是普通的对象成员函数。
一个插槽并不知道是否有任何信号与自己相连接。
而且,对象并不了解具体的通信机制。
用户可以将很多信号与单个插槽进行连接,也可以将单个信号与很多插槽进行连接,甚至将一个信号与另外一个信号相连接也是可能的,这时无论第一个信号什么时候发射,系统都将立刻发射第二个信号。
总之,信号与插槽构造了一个强大的部件编程机制。
图12.2所示为对象间信号与插槽的关系。
2.信号与插槽实现实例
(1)信号。
当某个信号对其客户或所有者内部状态发生改变时,信号就被一个对象发射。
只有定义了这个信号的类及其派生类才能够发射这个信号。
当一个信号被发射时,与其相关联的插槽将被立刻执行,就像一个正常的函数调用一样。
信号-插槽机制完全独立于任何GUI事件循环。
只有当所有的槽返回以后发射函数(emit)才返回。
如果存在多个槽与某个信号相关联,那么,当这个信号被发射时,这些槽将会一个接一个地执行,但是它们执行的顺序将会是随机的、不确定的,用户不能人为地指定哪个先执行、哪个后执行。
Qt的signals关键字指出进入了信号声明区,随后即可声明自己的信号。
例如,下面定义了3个信号:
signals:
voidmySignal();
voidmySignal(intx);
voidmySignalParam(intx,inty);
在上面的定义中,signals是Qt的关键字,而非C/C++的。
接下来的一行voidmySignal()定义了信号mySignal,这个信号没有携带参数;接下来的一行voidmySignal(intx)定义了重名信号mySignal,但是它携带一个整形参数,这有点类似于C++中的虚函数。
从形式上讲信号的声明与普通的C++函数是一样的,但是信号却没有函数体定义。
另外,信号的返回类型都是void。
信号由moc自动产生,它们不应该在.cpp文件中实现。
(2)插槽。
插槽是普通的C++成员函数,可以被正常调用,它们惟一的特殊性就是很多信号可以与其相关联。
当与其关联的信号被发射时,这个插槽就会被调用。
插槽可以有参数,但插槽的参数不能有缺省值。
插槽是普通的成员函数,因此与其他的函数一样,它们也有存取权限。
插槽的存取权限决定了谁能够与其相关联。
同普通的C++成员函数一样,插槽函数也分为3种类型,即publicslots、privateslots和protectedslots。
publicslots:
在这个区内声明的槽意味着任何对象都可将信号与之相连接。
这对于组件编程非常有用,用户可以创建彼此互不了解的对象,将它们的信号与槽进行连接以便信息能够正确地传递。
protectedslots:
在这个区内声明的槽意味着当前类及其子类可以将信号与之相连接。
这适用于那些槽,它们是类实现的一部分,但是其界面接口却面向外部。
privateslots:
在这个区内声明的槽意味着只有类自己可以将信号与之相连接。
这适用于联系非常紧密的类。
插槽也能够被声明为虚函数,这也是非常有用的。
插槽的声明也是在头文件中进行的。
例如,下面声明了3个插槽:
publicslots:
voidmySlot();
voidmySlot(intx);
voidmySignalParam(intx,inty);
(3)信号与插槽关联。
通过调用QObject对象的connect()函数可以将某个对象的信号与另外一个对象的插槽函数或信号相关联,当发射者发射信号时,接收者的槽函数或信号将被调用。
该函数的定义如下所示:
boolQObject:
:
connect(constQObject*sender,constchar*signal,constQObject*receiver,constchar*member)[static]
这个函数的作用就是将发射者sender对象中的信号signal与接收者receiver中的member插槽函数联系起来。
当指定信号signal时必须使用Qt的宏SIGNAL(),当指定插槽函数时必须使用宏SLOT()。
如果发射者与接收者属于同一个对象的话,那么在connect()调用中接收者参数可以省略。
信号与插槽相关联。
下例定义了两个对象:
标签对象label和滚动条对象scroll,并将valueChanged()信号与标签对象的setNum()插槽函数相关联,另外信号还携带了一个整型参数,这样标签总是显示滚动条所处位置的值。
QLabel*label=newQLabel;
QScrollBar*scroll=newQScrollBar;
QObject:
:
connect(scroll,SIGNAL(valueChanged(int)),label,SLOT(setNum(int)));
信号与信号相关联。
在下面的构造函数中,MyWidget创建了一个私有的按钮aButton,按钮的单击事件产生的信号clicked()与另外一个信号aSignal()进行关联。
这样,当信号clicked()被发射时,信号aSignal()也接着被发射。
如下所示:
classMyWidget:
publicQWidget
{
public:
MyWidget();
...
signals:
voidaSignal();
...
private:
...
QPushButton*aButton;
};
MyWidget:
:
MyWidget()
{
aButton=newQPushButton(this);
connect(aButton,SIGNAL(clicked()),SIGNAL(aSignal()));
}
(4)解除信号与插槽关联。
当信号与槽没有必要继续保持关联时,用户可以使用disconnect()函数来断开连接。
其定义如下所示:
boolQObject:
:
disconnect(constQObject*sender,constchar*signal,constObject*receiver,constchar*member)[static]
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 12 Qt 图形 编程 基础