QT的进一步学习 |
|
|
|
来源: ChinaUnix博客 日期: 2006.10.02 17:41 (共有条评论) 我要评论 | |
本文收录了互联网上关于Qt、Qte和Qtopia的介绍、开发环境及其搭建、应用编程等相关文档资源,在实际学习过程中还要多参考Qt自带的帮助文档、Qt和Qtopia中文论坛以及Trolltech公司网站的相关资料。希望能在前人的经验和官方资料的指导下,通过不断实践来加深自己的认识和理解。 QT简体中文:http://www.qiliang.net/qt.html,包括 Qt简体中文文档 、 Qt/Qtopia中文论坛 、 Trolltech公司 、 Qt中心(英文) 、 Qt论坛(英文) 、 Qt季刊(英文) 和 The Independent Qt Tutorial(在线英文电子书) 等资源的链接。 网络文章收录: QT简介: [url=#qthistory]QT简史[/url] 、 QT/QTE概述 ; QT开发概览: [url=#devintro01]LINUX系统中QT(qtopia)平台开发说明(文末有Qtopia应用程序的目录树)[/url] 、 [url=#devintro02]基于Qt/Embedded和Qtopia的GUI设计[/url] ; Qte/Qtopia的编译: 齐亮写的Qtopia安装文档 (包括PDA和Phone版本,还有Qtopia的启动脚本); 本站资料: Qtopia开发及应用平台的搭建 、 Build Qtopia for x86 、 Build Qtopia for arm ; [url=#insdev]嵌入式工具Qt/Qtopia的安装与使用[/url] (文章最后简要介绍了Qt应用开发过程); [url=#problem]交叉编译Qtopia可能碰到的问题及解决[/url] ; Qt的信号和槽机制: 信号和槽官方文档(中文); Qt的信号和槽机制介绍(IBM学习区) ; [url=#notes]Signals and Slots学习笔记[/url] ; [url=#qtintro]Qt的信号和槽[/url] ; Qt的内部进程通信机制 。 QT简史 [url=#indexdir]Return to the Top[/url] [来源]http://www-128.ibm.com/developerworks/cn/linux/guitoolkit/qt/overview/ 于明俭 , 自由撰稿人 2000 年 11 月 01 日 Qt 是一个跨平台的 C++ 图形用户界面库,由挪威 TrollTech 公司出品,目前包括Qt, 基于 Framebuffer 的 Qt Embedded,快速开发工具 Qt Designer,国际化工具 Qt Linguist 等部分 Qt 支持所有 Unix 系统,当然也包括 Linux,还支持 WinNT/Win2k,Win95/98 平台。 Trolltech 公司在 1994 年成立,但是在 1992 年,成立 Trolltech 公司的那批程序员 就已经开始设计 Qt 了,Qt 的第一个商业版本于 1995 年推出然后 Qt 的发展就很快了,下面是 Qt 发展史上的一 些里程碑:
基本上,Qt 同 X Window 上的 Motif,Openwin,GTK 等图形界 面库和 Windows 平台上的 MFC,OWL,VCL,ATL 是同类型的东西,但是 Qt 具有下列优点: 优良的跨平台特性: Qt支持下列操作系统: Microsoft Windows 95/98, Microsoft Windows NT, Linux, Solaris, SunOS, HP-UX, Digital UNIX (OSF/1, Tru64), Irix, FreeBSD, BSD/OS, SCO, AIX, OS390,QNX 等等。 面向对象 Qt 的良好封装机制使得 Qt 的模块化程度非常高,可重用性较好,对于用户开发来说是非常 方便的。 Qt 提供了一种称为 signals/slots 的安全类型来替代 callback,这使得各个元件 之间的协同工作变得十分简单。 丰富的 API Qt 包括多达 250 个以上的 C++ 类,还替供基于模板的 collections, serialization, file, I/O device, directory management, date/time 类。甚至还包括正则表达式的处理 功能。 支持 2D/3D 图形渲染,支持 OpenGL 大量的开发文档 XML 支持 但是真正使得 Qt 在自由软件界的众多 Widgets (如 Lesstif,Gtk,EZWGL,Xforms,fltk 等等)中脱颖而出的还是基于 Qt 的重量级软件 KDE 。 有趣的是,KDE 也是使得 Trolltech 公司承受巨大压力的一个原因。下面我们将来看看这场 著名的自由软件圣战 -- "KDE/QT .VS. Gnome/Gtk" 是怎么发生的。 在 Unix 的图形界面一向是以 MIT 的 X Window 系统为标准, 可是在商业应用上有两大流派,一派是以 Sun 公司领导的 Openlook 阵营,一派是 IBM/HP 领导的OSF (Open Software Foundation) 的 Motif, 双方经过多年竞争之后, Motif 最终 胜出,成为最普遍使用的界面库, 后来双方又妥协出一个 CDE(Common Desktop Enviroment) 作为一个标准的图形界面。 但是 Motif/CDER 的价格非常昂贵,在这同时微软的 Windows 图 形界面发展速度非常快,而 Unix 界的后起之秀 Linux 也急需一个可靠并且免费的图形界面。 1996 年 10 月,由开发图形排版工具Lyx的德国人 Matthias Ettrich 发起了 KDE 计划。 KDE 的全称为 K Desktop Environment,可以看出是针对 CDE。 KDE 本身 是采用 GPL 宣言的,但是 KDE 却是使用 Qt 来作为其底层库,因为当时 Qt 已经将其 Unix 版 本自由发布了,但是 Qt 并不遵循 GPL, 因此 KDE 被很多自由软件的作者攻击,认为利用非自 由软件开发违背了 GPL 的精神,于是 GNU 的狂热信徒兵分两路,一路是去制作 Harmonny,试 图重写一套兼容于 Qt 的替代品,另一路是由一个 26 岁的墨西哥程序员 Miguel De Icaza 领导 下重新开发一套叫 GNOME(GNU Network Object Enviroment)来替代 KDE。 由于 Linux 界的老大 RedHat 不喜欢 KDE/Qt 的版权,因此 RedHat 甚至专门派出了几个全职程序员来加入 GNOME 进行开发工作,于是一场同 Motif VS Openlook 相 似的圣战就这么打起来了。 Trolltech 为了 KDE 曾数次修改 Qt 的版权,从成立 KDE Free Qt 基 金会到采用 QPL,可谓是费尽心机,但是 GNOME 采用的 GTK 一开始就是完全的 GPL,因此在这个方 面 GNOME 有一定的优势,加上 Qt/KDE 采用 C++ 开发,入门的门槛比较高,而 GTK/Gnome 采用 C, 因此 GNOME 吸引了更多的自由软件开发者,但是 KDE 毕竟先走了一步, 推出的 KDE1.1.2 十分稳定, 而当时急忙中推出的 GNOME1.0 的系统稳定性奇差,有人甚至笑称 GNOME1.0 还没有 KDE 1.0 Alpha 稳定。但是 GNOME 后来发展比较快,大有迎头赶上的势头。 当时双方的开发者在网络 上炒得天翻地覆,连 Linux 之父 Linus 只是说了一句喜欢用 KDE 都倍受指责。 战争到了第三个年头,也就是2000年,可谓是风云突变,一个接 一个重大的事件先后发生: 首先是一批从 Apple 公司出来的工程师成立了一个叫 Eazel 的公司替 GNOME 设计界面,然后是一批 GNOME 程序员成立了一个 Helix Code 公司替 GNOME 提供商业支持,而大家期待以久的 KDE 2.0 也终于发布了,这恐怕是目前最为庞大的自由 软件了之一, 除了 KDE 本身,还包括 Koffice 套件,和集成开发环境 Kdevelop 等等大批软件,其 主力软件 Kounqueror 也是第一个可以同微软的 Internet Exploer 相抗衡的浏览器。 而 Sun 公司,Red Hat 公司, Eazel 公司,Helix Code 等一批公司成立了一个GNOME 基金会, Sun 还宣布将把重量级办公软件 Star office 同 GNOME 集成, Trolltech 公司自然不能坐以 待毙,于今年 10 月 4 日将 Qt 的 free edition 变为 GPL 宣言,彻底解决了 KDE 的版权问题, 又推出了嵌入式 Qt ,给了 GNOME 阵营一个有力的回击。 到现在为止,这场战争还在继续, 相信我们不能很快看到结果。一般说来, 目前 GNOME 吸引的公司比较多,但是 KDE/Qt 的开发的效率和质量比 GNOME 高,而且在 Office/嵌入式 环境中先走一步,在一定时间内还将处于优势地位。 那么对于用户来说,如何在 Qt/GTK 中作出选择呢?一般来说,如果用户使用 C++,对库的稳定性,健壮性要求比较高,并且希望跨平台开发的话,那么使用 Qt 是较好的选择, 但是值得注意的是,虽然 Qt 的 Free Edition 采用了 GPL 宣言,但是如果你开发 Windows 上的 Qt 软件或者是 Unix 上的商业软件,还是需要向 Trolltech 公司支付版权费用的。 LINUX系统中QT(qtopia)平台开发说明 [url=#indexdir]Retrun to Top[/url] ================================= 黄德智( [email protected] ) V:1.0 日期:20060305 QT 版本 :20060106V2 一、开发包的获取 ================ 最新的qtopia开发包名称为:qtopia_dev20060106V2.tar.gz; 当前版本为20060106V2,大小大约为 246M。 当前和后续版本也可在 东信 的FTP网站上下载得到. 二、开发环境的搭建 ================== qtopia开发包运行于LINUX操作系统上,LINUX要求内核版本为2.4.18或以上。安装LINUX操作系统, 如果只是在PC机上进行模拟器上的开发和交叉编译,可将LINUX安装到虚拟机上。将LINUX安装到虚拟机上, 建议硬盘空间最少分配6G,因为开发环境的搭建需要很大空间。 1、qtopia开发包的安装步骤: (1)、将qtopia_dev.tar.gz放到/opt/目录下 建议放在/opt/目录下,如果需要将开发包放到其他的目录,则解压后需要修改 qtopia-phone-2.1.1/src/下的config.pri文件。 (2)、用命令tar解压 在LINUX终端中CD到/opt/目录下,输入命令 tar -zxf qtopia_dev.tar.gz (回车), 几分钟后解压结束,出现命令提示符,如果报找不到什么目录或找不到什么文件,输入命令“df -m” 查看硬盘使用情况,一般原因是硬盘空间不足,解压失败,删除硬盘上不需要的文件或重新安装LINUX操作 系统,把分配硬盘空间更大。 (3)、解压后生成的目录为qtopia_dev 2、交叉编译工具的安装步骤: (1)、将cross2compiler.tar.gz放到/opt/目录下 (2)、用命令tar -zxf cross2compiler.tar.gz 解压 (3)、解压后生成的目录为debugdir 交叉编译工具的安装是为了编译出在真机上运行的可执行文件,如果只是在模拟器上开发,可不 进行交叉工具的安装。 三、模拟器上开发 ================ 1、配置文件和全局变量的设置: (1)、将.../qtopia_dev/.../x86/Settings目录放到/root/目录下,如果已经有/root/Settings目录, 则进行覆盖。 (2)、进入qtopia_dev/.../.../x86/目录。(中间目录省略) (3)、运行 ./setenv 设置环境变量,或source setenv命令。 2、编译例子工程: 在目录.../qtopia_dev/20060106V1102/x86/qtopia-phone-2.1.1/examples/application/下有一个名为 application的示例工程,对该工程进行编译和安装,可在模拟器中看到运行效果。 编译步骤: (1)、进入qtopia_dev/.../.../x86/qtopia-phone-2.1.1/examples/application目录; (2)、运行qmake生成Makefile 文件; (3)、运行make install 进行编译和安装。 3、模拟器上查看运行效果: (1)、进入.../qtopia_dev/20060106V1102/x86/目录 (2)、运行./rn.dat 启动模拟器 4、各个文件安装路径: (1)、所有编译后的程序放在 qtopia_dev/.../.../x86/qtopia-phone-2.1.1/image/opt/Qtopia/bin/目录下; (2)、程序相应的desktop文件放在 qtopia_dev/.../.../x86/qtopia-phone-2.1.1/image/opt/Qtopia/apps/Application/目录下 (3)、所有的程序图标放在qtopia_dev/.../.../x86/pia-phone-2.1.1/image/opt/Qtopia/pics/程序名/目录下 5、示例工程中的各个文件 (1)、application.pro文件 该文件中指定工程中用到的各个文件,文件包括:源代码文件(.c .cpp .h)、资源文件名、目标文件名、 desktop文件及路径、图标文件及路径、帮助文件及路径。 qmake命令在执行过程中访问application.pro文件将生成Makefile文件。 (2)、example.desktop文件 该文件将安装到模拟器和真机上,为了在运行时找到工程的各个文件。 (3)、example.control文件 文件中指定了工程的可执行文件路径、desktop文件路径、帮助文件路径和图标文件路径。 这个文件我现在还没有搞清楚:) (4)、examplebase.ui文件 该文件中以XML的格式定义了可视窗口上的各个部件。 (5)、其它文件 example.html和Example.png分别是工程的帮助文件和图标文件。 6、建立自己的工程 (1)、在示例工程的基础上建立自己的工程: 新建一个自己工程的目录,将示例工程中的相关文件复制到该目录,在示例工程的基础上进行修改。 需要复制的文件有:main.cpp、application.pro、example.control、example.cpp、example.h、example.desktop、 examplebase.ui、Example.png、example.html。 (2)、修改事项: application.pro文件: A、“application.pro”文件以.pro为扩展名,文件名要和它所在的目录名相同; B、11行,写上所有工程中用到头文件,文件名之间用空格隔开; C、12行,写上所有工程中用到.c或.cpp文件,文件名之间用空格隔开; D、13行,可视窗口文件名; E、14行,生成可执行文件名(目标文件名); F、18行,.desktop文件名; G、22行,图标文件名; H、23行,图标路径名,以目标文件名为目录名; I、26行,说明文件名; .desktop文件 A、7行,可执行文件名; B、8行,图标文件名; .ui文件,该文件可用QT开发环境修改,也可用文本编辑器修改, A、第2行,ExampleBase,“ExampleBase”可修改,但一定要与example.h文件的25行父类名称一致; main.cpp文件 A、该文件名不修改; B、21行,修改包含的头文件名; C、24行,修改类名; (3)、编译自己的工程 A、生成Makefile文件,在工程的目录下,运行qmake命令; B、编译,在工程的目录下,运行make命令; C、安装,在工程的目录下,运行make install命令; 四、为真机上运行进行编译 ======================== 将代码用arm编译器进行编译,目标文件就可在真机上运行。 1、必须进行交叉编译工具的安装; 2、将工程目录复制到.../qtopia_dev/20060106V1102/arm/qtopia-phone-2.1.1/目录下; 3、编译; 五、将文件安装到真机上 ====================== 1、设备间的软件连接: 连接步骤: (1)、连接好个连线 (2)、启动PC到LINUX系统中 (3)、关闭防火墙(service iptables stop) (4)、挂载进QT的开发包(mount /dev/hda4 /mnt/win) (5)、手动加载usbdnet.o(进入/mnt/win目录下,执行install),并在终端确认模块已经加载 (6)、启动(重起)nfs(service nfs restart) (7)、启动minicom(#minicom) (8)、到/home/目录下,执行脚本pc_linux (9)、在终端启动USB设备(ifconfig usb0 192.168.254.1 up) (10)、在minicom窗口中执行/home/目录下的linuxrc_run脚本 (11)、在minicom窗口中执行/home/目录下的脚本pc_linux (12)、检查目录/mnt/,是否和目录/opt/p2fs下的内容一致,若一致,则成功 这里提供几个诊断连接成功的方法: pc启动候,通过lsmod|grep usbdnet看看模块是否存在,如果不存在,请手动通过./install加载 如果以上都正确,连接手机,如果ifconfig -a 能看到设备而ifconfig 看不到,请按照文档设置ip并激活。 2、文件安装目录: 将编译好的程序、desktop文件和图标放在手机上/opt/Qtopia/各自对应的目录下。 (1)、所有编译后的程序放在 qtopia_dev/.../arm/qtopia-phone-2.1.1/image/opt/Qtopia/bin/目录下 (2)、程序相应的desktop文件放在 qtopia_dev/.../arm/qtopia-phone-2.1.1/image/opt/Qtopia/apps/Application/目录下 (3)、所有的程序图标放在 qtopia_dev/.../arm/qtopia-phone-2.1.1/image/opt/Qtopia/pics/程序名/ 目录下 3、重起手机。 六、手机目录: / (根目录) |----bin |----lib |----home |----mnt (对应于PC机的/opt/p2fs目录,该目录结构可随意) | |----opt | |----Qtopia | |----bin (放编译后的程序) | |----etc | |----pics (放图标文件) | |----apps | |--Applications (放desktop文件) | |----Applications (该目录结构任意,可存放工程自己的文件) 结束 ==== [url=#indexdir]Return to Top[/url] 基于Qt/Embedded和Qtopia的GUI设计 王丽洁,习勇,魏急波 (国防科技大学 电子科学与工程学院 湖南省 长沙市 410073) 摘要:介绍了Qt/Embedded和Qtopia的体系结构及其交叉编译过程与安装过程,重点讲述了如何实现Qt/Embedded下的应用程序以及如何将应用程序移植到Qtopia桌面系统。本文介绍的方法在笔者的开发平台上都做过验证。 关键词:嵌入式系统; GUI;Qt/Embedded; Qtopia; 交叉编译 中图分类号:TP368 Design of GUI Based on Qt/Embedded and Qtopia Wang Li-Jie, Xi Yong, Wei Ji-Bo (School of Electronic Science and Engineering, National University of Defense Technology, Changsha 410073, China) Abstract: In this paper, the architecture, cross-compiling and installation of Qt/Embedded and Qtopia are introduced. Especially, we focus on how to implement an application program based on Qt/Embedded and how to port an existing application to Qtopia desktop system. The methods introduced in the paper have all been validated in our development platform. Key words: Embedded System; GUI; Qt/Embedded; Qtopia; Cross-Compiling 1 引言 随着当前各种手持设备、无线设备及信息家电等嵌入式产品的迅猛发展,相应的嵌入式软硬件设计技术也在发生深刻的变化。如今,越来越多的嵌入式终端需要一个图形化的人机接口界面(GUI),良好的人机接口界面是嵌入式系统设计的一个关键技术,能够极大地提高人机交互的效率。本文详细阐述了在自行开发的嵌入式主板上实现某平台的图形显示终端过程。 2系统平台介绍 根据系统设计需求,本文目的是实现一个具有图形接口界面的嵌入式显示终端,该系统使用嵌入式系统设计技术。硬件上,使用自行开发的基于Samsung S3C2440A CPU的目标板,该CPU使用arm920T内核,其主频可达400Mhz;在软件上,选择嵌入式linux为操作系统,因为它源码开放,而且稳定性与安全性较高。 (uboot)、 设备驱动(包括帧缓存fb)、嵌入式Linux内核、文件系统(yaffs)、基于QT/Embedded和Qtopia的用户图形界面以及应用程序组成,系统平台结构如图(1)所示。 图(1) 系统平台软件结构图 3 Qt/Embedded和Qtopia 介绍及其开发环境的建立 目前嵌入式Linux的主流GUI系统主要有MiniGUI、Microwindows、OpenGUI、Qt/Embedded,这些GUI在接口定义、体系结构、功能特性存在很大差别,采取的技术路线也有所不同[1]。MiniGUI是建立在比较成熟的图形引擎之上,开发的重点在于窗口系统,其小巧精致并且尽量与Win32兼容。MicroWindows目前开发的重点在底层的图形引擎,窗口系统和图形接口方面功能比较欠缺,与Win32和X Windows窗口系统保持兼容,提供了相对完善的图形功能。OpenGUI基于一个用汇编实现的x86图形内核,提供了一个高层的C/C++图形/窗口接口,它的资源消耗小,可移植性差,不支持多进程。 Qt/Embedded是一个多平台的C++图形用户界面应用程序框架,其对象容易扩展,可移植性好,支持多个GUI平台的交互开发[2,3]。现在,Qt/Embedded被广泛地应用于各种嵌入式产品和设备中,从消费电器(如智能手机、机顶盒)到工业控制设备(如医学成像设备、移动信息系统等)。因此本文选择Qt/Embedded为本系统的GUI。 (1) Qt/Embedded和Qtopia体系结构 Qt/Embedded是Trolltech公司开发的面向嵌入式系统的Qt版本,与X11版本的Qt在最大程度上接口兼容,采用帧缓存(framebuffer)作为底层图形接口。Qt/Embedded类库完全采用C++封装,并且有着丰富的控件资源以及较好的可移植性,大范围的Qt/Embedded API可用于多种开发项目。Qt/Embedded的实现结构如图(2)所示: 图(2) Qt/Embedded实现结构 Qt/Embedded的底层图形引擎基于framebuffer。 framebuffer是一种驱动程序接口,它将显示设备抽象为帧缓冲区[4]。该驱动程序的设备文件一般是/dev/fb0、/dev/fb1等。对用户而言,它和/dev下的其他设备没有什么区别,用户可以把framebuffer看成一块内存,既可以从这块内存中读取数据,也可以向其中写入数据,而写操作立即反应在屏幕上。为运行Qt/Embedded,嵌入式Linux内核要支持framebuffer。 Qt/Embedded是Qt的面向嵌入式应用的简化版本,它包括一组完备的GUI类、操作系统封装、数据结构类、功能类和组合类。大部分Qt的应用程序可以经过简单的编译与重设窗口大小移植到Qt/Embedded。 Qtopia是基于QT/Embedded开发的一个嵌入式的窗口系统和应用程序集,如地址本、图像浏览、Media播放器等,还包括娱乐和配置工具,广泛用于PDA等掌上设备。 Qtopia平台由Qtopia 库(Qt/E,libqpe,libqtopia1,qtopiapim)和Qtopia server/laucher组成。Qtopia server/laucher是控制窗口系统、进程间通信、发起所有应用和其他核心任务的主要服务程序。 (2) Qt/Embedded和Qtopia的交叉编译与运行 整个GUI系统的构建需要对Qt/Embedded、Qtopia依次分别编链,然后有机地整合在一起。Qt/Embedded为Qtopia提供了底层支持,GUI系统的图形库窗口组建都由Qt/Embedded实现。 在构建GUI时用于Qt开发的典型工具如下: l tmake:跨平台的Makefile生成器。 l moc:用于Qt C++扩展的metra-object编译器。 l uic:从XML文件生成代码的用户界面编译器。 l designer:用于设计窗口组建的应用程序。 Qtopia的开发工具包SDK(Software Development Kit)是Qtopia开发环境的核心部分,编译后得到创建应用程序所需的软件包如下: l qvfb(virtual frame buffer):X窗口用来运行和测试Qtopia应用程序的系统程序。 l qpe(Qtopia executable):用来处理所有的用户程序界面[2,5]。 由于我们使用的是ARM CPU,因此需要对Qt/Embedded和Qtopia开发工具包进行交叉编译。本文使用arm-linux-gcc-3.3.2来建立交叉编译环境。为了对Qt/Embedded和Qtopia进行交叉编译,需要使用如下的源码树: l tmake-1.13.tar.gz:用来得到tmake工具。 l qt-embedded-2.3.7.tar.gz:Qt的嵌入式版本。 l qt-x11-2.3.2.tar.gz:Qt的X11版本。 l qtopia-free-1.7.0.tar.gz:官方网站提供的Qtopia免费版。 l e2fsprogs-1.38.tar.gz:为了得到qtopia所需的uuid.h和libuuid.so。 假设将上述源码树放在同一目录下,例如:/root/qtopia,并依次解压,然后进行编译,步骤如下: ①设定tmake的环境变量如下: export TMAKEPATH=/root/qtopia/tmake-1.13/lib/qws/linux-arm-g++ 此处指定了tmake在生成Makefile时使用arm交叉编译。 ②编译qt-x11,其目的是生成moc、uic、qvfb、designer,并将它们放在qt-embedded\bin目录下。 ③配置qt-embedded编译选项,命令为: ./configure -platform linux-arm-g++ -qconfig qpe -qvfb -depths 4,8,16,32. 此处-platform linux-arm-g++表示在arm平台上进行交叉编译;-qconfig local表示使用src/tools/qconfig-local.h;-depths 4,8,16,32表示需要qt支持的显示颜色深度。 ④使用make命令编译qt-embedded,用来生成Qt库(libqte.so)。 ⑤配置并交叉编译Qtopia,生成应用程序以及桌面环境。 假设编译完成后将qt和qtopia相关的库及所需文件分别存放于目标板文件系统的/opt/qt和/opt/qtopia下,运行Qtopia的方法是: ①设置QTDIR、QPEDIR和键盘鼠标等环境变量 export QTDIR=/opt/qt export QPEDIR=/opt/qtopia export QWS_KEYBOARD=USB:/dev/input/event1 export QWS_MOUSE_PROTO=USB:/dev/input/mouse0 ②开启qpe,也就是在Linux图形模式下执行/opt/qtopia/bin/qpe & 这样就可以在显示终端上看到qtopia桌面环境了。 4 Qt/Embedded和Qtopia下应用程序的实现 (1) Qt/Embedded应用程序的实现 Qt是一个创建GUI程序的C++类库,编写Qt应用程序的主要工作是基于已有的Qt类编写用户类。Qt应用程序的设计使用基于工程的方法,并通过.pro文件进行工程管理。实现应用程序的第一步是编写.pro文件,然后使用tmake根据该文件生成Makefile,最后进行源代码的编写。tmake的语法如下: tmake *.pro –o Makefile .pro的具体内容可以参考/qt/examples/下其他应用程序的.pro文件。 在本项目的研究中,需要涉及基本的窗口构建、应用程序的调用、图像背景的显示以及中文显示,下面对此进行详细阐述。 构建主窗口 Qt拥有众多的窗口部件,如按钮、菜单、滚动条和应用程序窗口等,它们组合起来可以创建各种用户界面。QWidget 是所有用户界面对象的基类,窗口部件是QWidget或其子类的实例。 创建主窗口先要在main.cpp函数中创建QApplication类型的对象。QApplication类管理图形用户界面应用程序的控制流和主要设置,它包含主事件循环,在其中来自窗口系统和其它资源的所有事件被处理和调度,它也处理应用程序的初始化和结束,并提供对话管理。对于任何一个使用Qt图形用户界面应用程序,都正好存在一个QApplication对象。然后定义主窗口变量,并通过QApplication类型的函数调用主窗口变量来启动主窗口。 创建主窗口部件最常用的方法是基于QWidget或QDialog类创建一个用户类。QDialog类是对话框窗口的基类,主要用于短期任务以及和用户进行简要通讯的顶级窗口。在本程序中使用QWidget类创建用户类,并使用户类通过公有继承派生于Qwidget类。 在构建窗口时需要注意用户界面的风格和布局。Qt提供了Windows、WindowsXP、Motif、MotifPlus、CDE、Platinum、SGI和Mac的内置风格。自定义风格可以通过继承QStyle、QCommonStyle或其他QCommenStyle类来完成。应用程序的风格可以如下设置: QApplication::setStyle(new MyCustomStyle) 在布局上Qt提供了布局管理器来组织父部件区域中的子部件,Qt内建的布局管理器有QHBoxLayout,QVBoxLayout和QGridLayout,而且布局也可以嵌套在任意层。例如使用QHBoxLayout(按行放置部件)的部件管理器为例在窗口水平放置两个按钮B1和B2的代码如下: QHBoxLayout *hbox = new QHBoxLayout(this); Hbox->addWidget(B1); Hbox->addWidget(B2); 创建按钮实现对应用程序的调用 Qt部件与用户的交互方式不同于其他的GUI工具包,其他的GUI工具包使用回调函数创建用户交互,但是Qt提供了信号/槽(signal/slots)[5]通信机制描述对象间的无缝通讯。槽(slot)是标准的成员函数,它能够连接到信号,每当槽所连接的信号被发射时,槽(函数)就被执行。信号(signal)是一种特殊类型的函数,都是返回void型,它们被定义为当某个事件发生时就被发射,之后执行所有被连接的槽。当定义信号时必须使用QT的宏SIGNAL(),定义槽时必须使用宏SLOT()。 通过调用QObject对象的connect函数可以将某个对象的信号与另一个对象的槽相关联,这样当发射对象发射信号时,接收对象的槽将被调用。该函数定义如下: bool QObject::connect(const QObject *sender,const char *signal,const QObject *receiver, const char *member) 与这个函数对应的disconnect函数,可以将信号和槽断开连接。 本文使用了QT库提供的按钮clicked()信号,自定义了槽函数run()来实现对应用程序的调用,并且定义了槽函数mycall()调用已经使用了特定参数的run()函数。 例如当一个按钮B1被点击时,它就发送“clicked”信号,通过connect()函数将信号与槽“mycall”连接起来,调用/opt/qt/examples/clock/下的应用程序“clock”的代码如下: void MyMainWindow::mycall() { MyMainWindow::run(“(cd /opt/qt/examples/clock; exec ./clock;)”); } connect(B1,SIGNAL(clicked()),this,SLOT(mycall())); 图像背景的显示 为了在Qt中装载和显示所支持的图像格式,需要创建一个QPixmap对象。QPixmap本质上是一个“屏幕外的部件(off-screen)”,图像可以先复制到一个QPixmap对象上,然后传送到QWidget。 QWidget部件使用如下的成员函数来为窗口添加图像背景: Public Members const QPixmap* [url=../../../%E5%BE%81%E6%96%87/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BE%81%E6%96%87/html/qwidget.html#9b2957#9b2957]backgroundPixmap[/url] () const virtual void [url=../../../%E5%BE%81%E6%96%87/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BE%81%E6%96%87/html/qwidget.html#7e417f#7e417f]setBackgroundPixmap[/url] ( const QPixmap & ) 例如有一幅名为flower.png的图片,将其设为背景的代码如下: QPixmap picture(“flower.png”) SetbackgroundPixmap(picture) 中文显示 Qt的中文显示是Qt国际化的一部分,“国际化”简称为i18n,用来提供一个架构,让同样的代码可以适用于各种语种习惯和编码系统,程序设计人员只要利用这个架构的机制、准则编写应用程序,就可以在不新编译代码的情况下,支持各种语言。 Qt支持Unicode—国际标准字符集,程序员可以在程序里自由的混用英语、汉语和其他Unicode所支持的语言。为Qt增加一种编码只需要增加该编码和Unicode的转化编码就可以了,Qt支持中文的GBK/Big5编码。 Qt支持的字体常用的是ttf和qpf。qpf是Qt/Embedded专用的一种适合嵌入式应用的字体,它属于位图字体,不可以缩放,而ttf字体可以缩放。默认情况下Qt/Embedded在lib/fonts目录下提供了一种可以显示中文的字体库UniFont,但是该字体库中没有ttf的字体。为了使用ttf字体显示中文,本文采取如下的方法:拷贝一种支持unicode编码的ttf字体到lib/fonts目录下,例如,windows系统下的宋体simsun.ttf;同时还需要在此目录的fontdir脚本中添加下面一行: simsun simsun.ttf FT n 50 0 su fontdir脚本用来向系统注册所支持的字体,它的每一行定义了一种字体的设置,其格式如下: 字体名称>字体文件名>字体渲染类型>是否斜体>尺寸>字体标志>[尺寸列表] 在程序设计中,首先指定编码方式以支持中文: QTextCodec *code=QTextCodec::codecForName("GBK") 接着为部件(例如Mywidget)执行 Unicode的转化编码: QString uniStr=code -> toUnicode("要显示的中文字符") Mywidget-> setFont(QFont("simsun",20,QFont::Bold)) Mywidget-> setText(uniStr) (2) 向Qtopia移植应用程序 Qtopia是一个基于QT/Embedded开发的一个嵌入式的桌面环境和应用程序集,可以方便地在Qtopia桌面环境中添加用户应用程序或者对桌面进行配置。为了添加用户应用程序,需要在Qtopia/apps/Application目录下建立一个.desktop的桌面文件,该文件指明了桌面文件的图标以及应用程序的入口点。应该注意的是为了让新的应用程序在Qtopia桌面环境中运行,应用程序必须使用QT提供的图形库进行编译。 下面以移植嵌入式的web浏览器konqueror为例,说明添加新的应用程序的过程。 (1)下载konqueror源代码,对其进行交叉编译。为了支持中文显示和flash,还需要进行必要的源代码修改,并且加入相应的插件。 (2)将konqueror.png图标文件拷贝到在/opt/Qtopia/pic/下。 (3)将konqueror可执行文件放于/opt/konqueror/下,然后在Qtopia/apps/Application目录下建立konqueror.desktop文件,具体内容可参考qtopia自带的.desktop文件。需要注意的是konqueror.desktop的Exec项应指明可执行文件的具体位置。例如:Exec=/opt/konqueror/bin/konqueror (4)导出konqueror的运行环境变量,就可以直接在桌面上点击其图标浏览网页了。 5 结束语 嵌入式产品的广泛应用带动了图形用户界面(GUI)的迅速发展,嵌入式系统需要一个高性能、高可靠的GUI的支持。基于Qt/Embedded的Qtopia桌面系统为系统用户提供了良好的使用和交互环境。本文系统介绍了基于Qt/Embedded开发应用程序的方法以及将现有的应用程序移植进Qtopia的具体过程,为类似的系统开发供了一个参考。 参考文献 [1] 丁丁,习勇,魏急波,三种主流嵌入式图形用户界面的移植及性能比较,《电子产品世界》,2004,9(5):73-74 [2] QT 3.3白皮书, http://www.trolltech.com [3] 袁鹏飞,24小时学通qt编程,北京:人民邮电出版社,2000/11 [4] 于明俭,陈向阳,方汉, LINUX程序设计权威指南,北京:机械工业出版社,2001/4 [5] 张娟,张雪兰,基于嵌入式的GUI应用程序的实现,《计算机应用》,2003,23(4):115-117 作者简介:王丽洁(1981-),女,硕士研究生,研究方向为嵌入式系统的开发与应用。 作者联系方式:410073,湖南长沙国防科大四院2系,0731-4575752,13873163041, [email protected] 交叉编译Qtopia的几个问题 [url=#indexdir]Return to Top[/url] [http://blog.csdn.net/chenrongqin/archive/2005/08/28/466936.aspx] 本文记载步骤和排错过程,用到的源码包有:qt-embedded-2.3.10-free.tar.gz、qtopia-free-source-2.1.1.tar.gz、jpegsrc.v6b.tar.gz、e2fsprogs-1.38.tar.gz。 步骤主要有: 1. 注意设置环境变量:QTEDIR、QPEDIR、PATH、LD_LIBRARY_PATH等 2. 交叉编译qte: ./configure –qconfig qpe -system-jpeg -no-xft -gif -qvfb -depths 4,8,16,32 –xplatform linux-arm-g++ 3. 交叉编译qtopia ./configure -edition pda -no-qtopiadesktop -displaysize 480X640 –xplatform linux-arm-g++ 在编译过程中遇到的主要错误有: 1. usr/local/hybus-arm-linux-R1.1/arm-linux/bin/ld: warning: libjpeg.so.62, needed by /usr/local/qt-2.3.10/lib/libqte.so, not found (try using -rpath or -rpath-link) /usr/local/qt-2.3.10/lib/libqte.so: undefined reference to `jpeg_read_scanlines'........................... 解决方法: 从http://freshmeat.net/redir/libjpeg/5665/url_tgz/jpegsrc.v6b.tar.gz下载jpegsrc.v6b.tar.gz解压并进入解压的目录。 tar -xzf jpegsrc.v6b.tar.gz cd jpeg-6b [root@localhost jpeg-6b]# ./configure --enable-shared 修改生成的Makefile文件: prefix = qt-embeddedd的文件夹路径(eg: /usr/local/qt-2.3.10) CC= /usr/local/arm/2.95.3/bin/arm-linux-gcc AR= /usr/local/arm/2.95.3/bin/arm-linux-ar rc AR2= /usr/local/arm/2.95.3/bin/arm-linux-ranlib 保存 在你的qt-embedded中建立man/man1文件家,否则安装jpeg库时会出错 [root@ localhost jpeg-6b]#mkdir -p qt-2.3.10/man/man1 最后: [root@ localhost jpeg-6b]# make [root@ localhost jpeg-6b]# make install 就可以给qt-embedded添加好for arm的libjpeg库. 把生成的库文件向下面的目录中也拷贝一份: /usr/local/hybus-arm-linux-R1.1/arm-linux/lib /usr/lib 2. qpeapplication.cpp: In method `void QPEApplication::systemMessage(const QCString &, const QByteArray &)': qpeapplication.cpp:2175: warning: unused variable `int old'............... 解决方法:在$QPEDIR/src/libraries/qtopia目录下执行 cp custom-linux-ipaq-g++.cpp custom-linux-myarm-g++.cpp cp custom-linux-ipaq-g++.h custom-linux-myarm-g++.h 3. 遇到关于libuuid的错误时................. 解决办法:交叉编译e2fsprogs-1.38: ./configure –host=arm-linux –with-cc=arm-linux-gcc –with-linker=arm-linux-ld –enable-elf-shlibs –prefix=/usr/local/qtopia-2.1.1 make make install 这样就将libuuid库安装到了qtopia目录下。 再执行cp –r /usr/local/e2fsprogs-1.38/lib/uuid /usr/local/qtopia-2.1.1/include 使得qtopia能够找到头文件。 嵌入式工具QT的安装与使用 [url=#indexdir]Return to Top[/url] [http://blog.csdn.net/suisuibianbian/archive/2004/11/18/185923.aspx] Qt是Trolltech公司的一个产品。Trolltech是挪威的一家软件公司,主要开发两种产品:一种是跨平台应用程序界面框架;另外一种就是提供 给做嵌入式Linux开发的应用程序平台,能够应用到PDA和各种移动设备上。Qt和Qtopia分别是其中具有代表性的两个。 Qt是一个多平台的C++图形用户界面应用程序框架,它能给用户提供精美的图形用户界面所需要的所有元素,而且它是基于一种面向对象的思想,所以用户对其对象的扩展是相当容易的,并且它还支持真正的组件编程。 Qt是Linux桌面环境KDE的基础。笔者认为,可以说Qt与Windows下的Mfc的实质是一样的,所以Qt最大的优点在于其跨平台性,可以支持现有的多种操作系统平台,主要有: ◆ MS/Windows 95、Windows 98、WindowsNT 4.0、Windows 2000、Windows XP; ◆ Unix/X11 Linux、Sun Solaris、HP-UX、Compaq True64Unix、IBM AIX、SGI IRIX和很多其它X11平台; ◆ Macintoshi Mac OSX; ◆ Embedded—带FramBuffer的Linux平台。 下面简单介绍一下Qt/Embedded和Qtopia在Linux上的安装和使用,还有在开发过程中可能碰到的一些问题。 Qt 和Qtopia的安装 如果需要安装一个带FramBuffer的Qtopia平台,需要有以下软件(所列举软件以笔者使用的为例): ◆ Qtopia 1.6.0; ◆ Tmake 1.11; ◆ Qt/Embedded 2.3.4(Qtopia 1.6.0是基于该开发平台上开发的); ◆ Qt/Embedded 2.3.2 for X11; ◆ Qt 3.1.2 for X11。 在Trolltech公司的网站上可以下载该公司所提供的Qt/Embedded的免费版本。 Qtopia平台安装分为以下几个步骤: 1. 解包Qtopia 在Linux命令模式下运行以下命令: tar xfz qtopia-source-1.6.0 (解包) cd qtopia-source-1.6.0 export QPEDIR=$PWD (设置环境变量) cd.. 2. 安装Tmake 在Linux命令模式下运行以下命令: tar xfz tmake-1.11.tar.gz export TMAKEDIR=$PWD/tmake-1.11 export TMAKEPATH=$TMAKEDIR/lib/qws/linux-x86-g++ export PATH=$TMAKEDIR/bin:$PATH 3. 安装Qt/Embedded2.3.4 在Linux命令模式下运行以下命令: tar xfz qt-embedded-2.3.4-commercial.tar.gz cd qt-2.3.4 export QTDIR=$PWD export QTEDIR=$QTDIR export PATH=$QTDIR/bin:$PATH export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH cp $QPEDIR/src/qt/qconfig-qpe.h src/tools/ . /configure -qconfig qpe -qvfb -depths 4,8,16,32 make sub-src cd .. 也可以在configure的参数中添加-system-jpeg和gif,使Qtopia平台能支持jpeg、gif格式的图形。 4. 安装Qt/X11 2.3.2 在Linux命令模式下运行以下命令: tar xfz qt-x11-2.3.2-commercial.tar.gz cd qt-2.3.2 export QTDIR=$PWD export PATH=$QTDIR/bin:$PATH export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH . /configure -no-opengl make make -C tools/qvfb mv tools/qvfb/qvfb bin cp bin/uic $QTEDIR/bin cd .. 根据开发者本身的开发环境,也可以在configure的参数中添加别的参数,比如-no-opengl或-no-xfs,可以键入./configure -help来获得一些帮助信息。 5. 安装Qt/X11 3.1.2 在Linux命令模式下运行以下命令: tar xfz qt-x11-commercial-3.1.x.tar.gz cd qt-x11-commercial-3.1.x export QTDIR=$PWD export QT3DIR=$QTDIR export PATH=$QTDIR/bin:$PATH export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH ./configure -thread make cd .. 6. 安装Qtopia 在Linux命令模式下运行以下命令: cd qtopia-source-1.6.x export QTDIR=$QTEDIR export QPEDIR=$PWD export PATH=$QPEDIR/bin:$PATH cd src ./configure make cd ../.. 7. 安装Qtopia桌面 cd qtopia-source-1.6.x/src export QTDIR=$QT3DIR ./configure -qtopiadesktop make mv qtopiadesktop/bin/qtopiadesktop ../bin cd .. Qt和Qt Designer的使用 根据上面的步骤安装完成了Qt/Embedded和Qtopia之后,就可以运行这些程序了。 运行Qt的虚拟仿真窗口:在Linux的图形模式下运行命令qvfb&;Qtopia只是一个用Qt/Embedded开发的程序,运行Qtopia,在图形模式下运行命令: export QTDIR=$QTEDIR, qpe &; 这样Qtopia的程序就运行在QVFB上,即Qt的虚拟仿真窗口。 Qt/Embedded是针对嵌入式Linux而开发的一种开发工具,Qt封装了一些常用的类,而且这些类的名字都以Q字开头命名,如QString、QDialog等。这里主要介绍一下如何利用Qt Designer来设计组件,并生成相应的代码。 在Qt中,把组件分为复合体、原始体和配件。而在Qt中,组件是由一些抽象类、复杂的组件类、管理组件几何特性的类等组成。 Qt中有三个主要的基类:QObject、Qapplication和QWidget。 在Qt 中编程,利用Signal和Slot进行对象之间的通信是Qt的主要特征。它与Windows中的消息机制非常类似,但是Signal和Slot机制真正 实现了一种消息的封装。当对象的状态改变时,发出Signal,通知所有的Slot接受Signal,尽管它不知道哪些函数是Slot,Slot一开始也 不知道哪些Signal可以接收。Signal和Slot之间不是一一对应的关系,一个Signal可以发给多个Slot, Slot也可以接收多个Signal。Slot除了可以接收Signal以外,与其它的成员函数没有区别。这种机制比使用回调函数要灵活,但是会减慢程序 的运行速度。不过在现在高速CPU的面前,这种损失是无足轻重的,而且它还能保证程序的简明性和灵活性,非常便利。 在Qt的组件中,不仅定义了常用的成员变量和成员函数,还定义了所有与该组件相关的Signal和Slot。 要将组件组合起来,最简单的方法就是使用Qt Designer。首先要启动Qt Designer,在Linux命令模式下,键入以下命令(假设Qt安装在/usr/local下): cd qt-2.3.2/bin ./designer 这样就可以启动一个与Windows下的Delphi相类似的如图1的界面。 然 后新建一个QFrame,将自己需要的组件直接拖拉到这个Frame中,相信很多人都有过这样的经历,此处就不再详细描述了。完成之后存盘时,会将这个新 的组件保存为一个扩展名为.ui的文件。假设所存的文件名为test.ui,用vi test.ui来查看这个文件,发现这是一个用xml语言写的一个文本。下面用这个test.ui生成相应的test.h和test.cpp。同样还是在 这个目录下,可以看到一个uic的工具,这个是Qt专门用来将ui文件生成.h和.cpp文件的,在终端模式下键入以下命令: ./uic -o test.h test.ui ./uic -o test.h -i test.cpp test.ui 此 时就能看到生成了相应test.h和test.cpp,这是一个类。当然这只是一些表面的东西,还需要在这些代码中添加相应的Signal和Slot,完 成所需要的操作。值得注意的是,相应版本生成的ui最好用相应版本的uic来生成代码。如果用Qt 3.1.2的Designer生成的ui,用Qt 2.3.2的uic来生成代码,生成的代码都会是一些空函数。 在一般的开发过程中,首先通过这个ui生成的一个类,在Qt中通常叫做 Base,如上面的例子,叫做testBase;然后再新建一个类,来继承这个Base。通常叫做实现类Impl,如testImpl。在这个实现类里面 定义所需要的成员函数、Signal和Slot,因为ui可能是经常需要改动的。如果这样做,每次只需要在Designer中修改ui,而不用去理会这些 成员函数、Signal和Slot了。 编译一个Qt程序必然需要Makefile,在Qt中提供了一个专门生成Makefile的工具,就是tmake。用tmake需要根据编写的程序写一个.pro文件。.pro文件非常简单,有固定的格式,下面是一个例子: TEMPLATE = app CONFIG = qtopia warn_on release MOC_DIR =tmp OBJECTS_DIR =tmp HEADERS =fcrs.h\ structs.h \ globalfunc.h \ globalvars.h \ testimpl.h SOURCES = main.cpp \ globalfunc.cpp\ globalvars.cpp \ testimpl.cpp INTERFACES = test.ui \ TARGET = fcrs 生成这个.pro文件之后,在终端中键入下面的命令: tmake -o Makefile test.pro 就自动生成了一个Makefile,使用这个Makefile编译所编写的程序就可以了。 Signals and Slots in Depth [url=#indexdir]Return to Top[/url] [http://blog.csdn.net/suisuibianbian/archive/2004/11/18/185923.aspx] 信号和槽机制是Qt编程的基础。它使应用程序员能够在对象之间互相不知道任何联系的情况下把这些对象帮定在一起。我们已经连接了一些信号和槽,声明了我们自己的信号和槽,执行自己的槽,发出自己的信号。让我们花点时间深入点看这个机制。 槽与普通的C++成员函数几乎一样。它们可以是virtual,overloaded,public,protected,private,并且可以被其他C++成语函数直接调用。区别是槽可以被信号连接,信号被发送将它自动调用。 connect()声明如下: connect(sender, SIGNAL(signal), receiver, SLOT(slot)); sender和receiver是指向QObjects的指针,signal和slot是没有参数名的函数signatures(签名)。SIGNAL()和SLOT()宏将转换它们的参数成为一个字符串。 刚才的例子中我们总是连接不同的信号到不同槽,有更多的可能去探究: 1 . 一个信号可以连接很多槽: connect(slider, SIGNAL(valueChanged(int)),spinBox, SLOT(setValue(int))); connect(slider, SIGNAL(valueChanged(int)),this, SLOT(updateStatusBarIndicator(int))); 当信号发出,这些槽一个接一个被调用,以任意顺序。 2 . 很多信号可以连接到同样的槽: connect(lcd, SIGNAL(overflow()),this, SLOT(handleMathError())); connect(calculator, SIGNAL(divisionByZero()),this, SLOT(handleMathError())); 其中任何一个信号发出时,这个槽被调用。 3. 一个信号可以连接到另一个信号: connect(lineEdit, SIGNAL(textChanged(const QString &)),this, SIGNAL(updateRecord(const QString &))); 当第一个信号发出,第二个信号也会发出.从这点,singnal-singnal连接与signal-slot连接不能区分. 4 . 连接可以被移除: disconnect(lcd, SIGNAL(overflow()),this, SLOT(handleMathError())); 这个很少需要,因为Qt在对象删除的时候自动删除所有包括对象的连接. 当连接一个信号到一个槽(或者连接到另一个信号),它们都必须用同样的顺序有同样的参数: connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),this, SLOT(processReply(int, const QString &))); 异常情况,如果信号比它连接的槽更多的参数,增加的参数将直接忽略: connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),this, SLOT(checkErrorCode(int))); 如果参数类型不符合,或者信号或槽不存在,Qt将输出一个运行时警告.同样,如果参数名被包含在信号或槽signatures(签名)里,Qt将给一个警告. QT中的信号和槽 [url=#indexdir]Return to Top[/url] [http://blog.csdn.net/compiler_hdz/archive/2006/02/13/598163.aspx] 在图形界面编程中,很多时候我们希望一个可视对象发生某种变化时通知另一个或几个对象,再一个地说,我们希望任何一 类的对象能和其他对象进行通讯。例如,某个数值显示窗口负责显示某个滚动条对象的当前数值,当滚动条对象的值发生变化时,我们希望数值显示窗口能收到来自 滚动条对象发送的“数值改变”的信号,从而改变自己的显示数值。 对于类似以上的问题,较早的工具包使用“回调”的方式来实现。回调是指一个函数的指针,如果你希望一个处理函数同志你一些事件,你可以把另一个函数的指针传递给处理函数。处理函数在适当的时候会调用回调函数。 采用回调方式实现对象间的通讯有两个主要缺点,首先回调函数不是类型安全的,我们不能确定处理函数使用了正确的参数来调用回调函数,第二,回调函数和处理函数间的联系非常紧密,因为处理函数必须知道要调用哪个回调函数。 在QT开发环境中,实现对象间的通讯我们有一种称为“信号和槽”的机制可以代替回调函数。信号和槽机制用于实现对象间的通讯,是QT的一个中心特性,恐怕也是QT与其它工具包最不同的地方了。 信号和槽机制就是:当一个特定的事件发生时,一个或几个被指定的信号就被发射,槽就是一个返回值为void的函数,如果存在一个或几个槽和该信号相连接,那在该信号被发射后,这个(些)槽(函数)就会立刻被执行。 信 号和槽机制是类型安全的,一个信号的签名必须与它的接收槽的签名相匹配,这样编译器就可以帮助我们检查类型是否匹配。信号和槽是很宽松的联系在一起的,一 个发射信号的对象不用考虑哪个槽会接收这个信号,接收信号的槽的所在对象也不知道要连接的信号是哪个对象发射的。QT的信号和槽机制可以保证如果你把一个 信号和一个槽连接起来后,槽会在正确的时间使用信号的参数而被调用,信号和槽可以使用任何数量、类型的参数。 QT的窗口部件已经有很多预定 义的信号,也有很多预定义的槽,但我们总是通过继承来加入我们自己的信号和自己的槽,这样我们就可以处理感兴趣的信号了。凡是从QObject类或者它的 某个子类继承的所有类都可以包含信号和槽。当某个事件发生后,被指定的信号就会被发射,它不知道也没有必要知道是否有槽连接了该信号,这就是信息封装。 槽 是可以用来接收信号的正常的对象的成员函数,一个槽不知道它是否被其它信号连接。可以把一个信号和一个槽进行单独连接,这时槽会因为该信号被发射而被执 行;也可以把几个信号连接在同一个槽上,这样任何一个信号被发射都会使得该槽被执行;也可以把一个信号和多个槽连接在一起,这样该信号一旦被发射,与之相 连接的槽都会被马上执行,但执行的顺序不确定,也不可以指定;也可以把一个信号和另一个信号进行连接,这样,只要第一个信号被发射,第二个信号立刻就被发 射。 下面看看不使用信号和槽和使用信号和槽的两个对象有什么不同。 不使用信号和槽的C++对象: class NoSignalClass { public: NoSignalClass (void) {} int value (void) const {return _value;} int setValue (int value) {_value = value;} private: int _value; } 使用信号和槽的QT对象: class UseSignalClass { Q_OBJECT public: UseSignalClass (void) {} int value (void) const {return _value;} public slots: int setValue (int value) {_value = value;} signals: void valueChanged (int); private: int _value; } 这 两个类有相同的内部状态,相同的公有方法,但后一个类却支持使用信号和槽的组件编程:这个类可以通过发射一个信号(valueChanged())来告诉 外界它的状态发生了变化,并且它有一个槽,可以接收信号。所有包含信号和槽的类都必须在它们的声明中提到Q_OBJECT。槽要由自己来实现,这里最好把 int setValue (int value)槽这样实现: void UseSignalClass::setValue( int value ) { if ( value != _value ) { _val = value; emit valueChanged(value); } } emit valueChanged(value);这行代码是发射一个信号valueChanged。 要想使一个槽在一个信号被发射后被执行,要显示地进行连接: UseSignalClass a,b; connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int))); 这样做后,就把对象a的信号valueChanged和对象b的槽setValue连接了起来,当a的信号valueChanged被发射后,对象b的槽setValue马上就被执行。 执行以下这句代码: b.setValue( 10 ); 这句被执行后,对象b的valueChanged信号会被发射,但没有槽和该信号相连,所以什么也没做,信号被丢弃。 a.setValue( 80); 这句被执行后,对象a的valueChanged信号会被发射,该信号有槽(对象b的setValue)相连,所以b.setValue(int)马上被执行,且参数为80,所以b.value()的值为80。 如果要解除已经建立好连接的信号和槽,可以使用disconnect()函数。bool QObject::disconnect ( const QObject * sender, const char * signal, const Object * receiver, const char * member ) [static] 这个函数断开发射者中的信号和接收者中的槽函数之间的关联。有以下三种情况: 1、断开某个对象与其它对象的任何连接: disconnect(object, 0, 0, 0);或object->disconnect(); 2、断开某个信号与其它任何槽的连接: disconnect(object, SIGNAL(signal()), 0, 0);或object->disconnect(SIGNAL(signal())); 3、断开两个对象之间的任何关联: disconnect(object, 0, receiver, 0);或object->disconnect(receiver); 在disconnect函数中0可以用作一个通配符,可分别表示任何信号、任何接收对象、接收对象中的任何槽函数。但是发射者不能为0,其它三个参数都可以为0。 关 键字signals指出随后开始信号的声明,这里signals用的是复数形式而非单数,siganls没有public、private、 protected等属性,这点不同于slots。另外,signals、slots关键字是QT自己定义的,不是C++中的关键字。 这个例子说明,采用QT的信号和槽机制后,对象之间可以在相互不知道的情况下一起工作,只要在最初的时候在他们中间建立了连接。 槽也是普通的C++函数,可以一样被调用,他唯一的特点就是 他们可以被信号连接。因为槽就是普通的成员函数,它们也和普通的成员函数一样有访问权限,一个槽的访问权限决定了哪些信号可以和它相连接,而信号就没有访问权限的概念。 一个public slots:区包含了任何信号都可以相连的槽。你生成了许多对象,它们互相并不知道,把它们的信号和槽连接起来,这样信息就可以正确地传递,并且就像一个铁路模型,把它打开然后让它跑起来。 一个protected slots:区包含了之后这个类和它的子类的信号才能连接的槽。这就是说这些槽只是类的实现的一部分,而不是它和外界的接口。 一个private slots:区包含了之后这个类本身的信号可以连接的槽。这就是说它和这个类是非常紧密的,甚至它的子类都没有获得连接权利这样的信任。 也可以把槽定义为虚函数,这也很有用。 使用信号和槽机制,要注意以下问题: 1、信号和槽的机制是非常有效的,但是它不像“真正的”回调那样快。信号和槽稍微有些慢,这是因为它们所提供的灵活性。但这种损失相对来说是比较小的。但要追求高效率的话,比如在实时系统中就要尽量少用这种机制。 2、信号和槽机制与普通函数的调用一样,如果使用不当的话,在程序执行时有可能形成死循环,所以,在定义槽函数时一定要注意避免间接形成无限循环,即在槽中再次发射所接收到的同样的信号。 3、如果一个信号和多个槽相关联的话,那当这个信号被发射时,与之相关联的槽的执行顺序将是髓机的,且顺序不能指定。 4、宏定义不能用在signal和slot的参数中。 5、构造函数不能用在signals和slots声明区域内。 6、函数指针不能作为信号或槽的参数。 7、信号和槽不能有缺省参数值。 8、信号和槽不能携带模板类参数。 9、嵌套的类不能位于信号和槽区域内,也不能有信号或者槽。 10、友元声明不能位于信号和槽的声明区域内 |