17实验十七+Linux环境下Qt编程实验.pdf
实验十七实验十七 Linux 环境下环境下 Qt 编程实验编程实验 一、实验目的一、实验目的 1、掌握简单的 Linux 应用程序的编程;2、了解嵌入式 Linux GUI 编程环境;3、掌握 Qt 程序设计方法;二、实验内容二、实验内容 1、使用 Qt 类库编写 helloworld 程序;2、使用交叉编译工具编译 helloworld 程序;3、下载并调试 helloworld 应用程序;三、实验设备三、实验设备 1、硬件:1、硬件:PC 机;DM2410 实验系统;串口线;机对机网线;2、软件:2、软件:PC 机操作系统(WINDOWS2000,REDHAT LINUX);Linux 下 ARM GCC 交叉编译环境;实验系统附带文件;tmake 软件包;Qt 移植实验中的安装的共享类库软件包;四、预备知识四、预备知识 1、C 语言的基础知识;2、程序调试的基础知识和方法;3、Linux 的基本操作。五、基础知识五、基础知识 Qt 是用来开发多平台 GUI 和应用程序的 C+工具包。它除了提供 C+类库之外,还提供了很多工具,使得开发更加快速容易。Qt 支持多平台和国际化。因此,Qt 开发出来的程序有更强的适应性。Qt C+工具包自从 1995 年开始就已经成为许多商业软件的核心。Qt 被很多公司使用,如:AT&T,IBM,NASA,Xerox 和其他的一些小公司和组织。在保持易用性和强大功能的前提下,Qt 3.1 增加了很多重要的函数和新的类。Qt 的类尽可能的降低开发者的工作量,并且提供统一的接口以利于学习,Qt 是完全面向对象的。1、Qt 概要 Qt 包含了丰富的部件(在 Windows 术语中叫控件),这些部件提供了一些标准的功能。Qt 引进了一种创造性的内建对象通信机制-信号和槽,来代替过去不安全的回调技术。Qt 还提供了传统的事件模型来处理鼠标按下,键盘敲击等事件。Qt 多平台应用可以使用现代程序设计需要的所有用户界面功能,如:菜单,上下文菜单,拖放,和 可停靠工具栏等。直观的命名规则和统一的设计方法简化了程序的设计。Qt 提供了一个视觉化的用户界面设计工具-Qt Designer。它支持 Qt 强大的布局功能。它可以用来设计 GUI 或者 通过内建的 C+代码编辑器创建一个完整的程序。Qt 可以很好的支持二维和三维的图形。它是平台无关 OpenGL 设计 GUI 工具包的事实标准。Qt 可以使用标准的数据库创建平台无关的数据库应用。它内建了很多数据库的驱动,如:Oracle,Microsoft SQL Server,Sybase Adaptive Server,PostgreSQL,MySQL,和 ODBC-compliant 等。Qt Designer 完全整合了 Qt 的数据库功能,因此它可以提供数据库资料预览。包含了数据库的 Qt 相关部件,内建和自定制的部件都可以增加数据感知功能。利用 Qt 的风格和主题,Qt 在很多支持的平台上具有本地外观。一份源代码,只需要重新编译就可以运行于 Windows(95,98,NT4,ME,2000,XP),Mac OSX,Linux,Solaris,HP_UX 和其他支持 X11 的各种 Unix 系统。Qt 程序也可以运行于嵌入 Qt 的系统。Qt 的 qmake 工具可以生成于目标平台相适应的 makefiles 或者.dsp 文件。许多用户在单一平台下使用 Qt 开发,如:Windows,Mac OS X 和 Unix 系统。既然Qt 的体系结构利用了平台底层的属性,所以它在特定的平台下会支持该平台特有的属性,例如:在 Windows 下支持 ActiveX,在 Unix 下支持 Motif。Qt 使用 Unicode 并且提供了大量的国际化支持。Qt 提供 QtLinguist 和 其它的一些工具来支持翻译。应用程序的文本可以很容易的使用和混合许多 Unicode 支持的语言,如:阿拉伯文,中文,英文,希伯莱文,日文,俄文等。Qt 为许多特定的领域提供了特定的类。例如:XML 模块包括了 SAX 和 DOM 解析器。通过使用与 STL 兼容的 Qt 集成类,对象可以保存在内存中。通过标准的协议,Qt 的输入输出和网络类支持本地和远程的文件处理。通过使用插件和动态库,Qt 应用程序可以扩展他们的功能。插件提供了额外的解码器,数据库驱动,图像格式,风格和部件等。动态库可以提供没有范围约束的功能。这些插件和动态库作为产品来出售。Qt 是一个被广泛使用的成熟的 C+工具包。除了在商业上的应用之外,免费版本的Qt 还是 Linux 桌面环境的基础。Qt 的多平台开发系统,视觉化的开发方式,和高效的 API将使得应用程序的开发更轻松,更具有乐趣。在线参考:http:/ Qt 具有一系列丰富的部件(按钮,卷轴等)可以满足很多应用。如果有特殊的需要,你可以很容易和灵活派生出子类。Qt 提供了很多部件。所谓部件就是一个视觉元素,它们做和在一起来构成用户界面。按钮,菜单,卷轴,消息窗口和 应用程序窗口都是部件的例子。Qt 的部件并不严格区分控件和容器;所有的部件即可以用作控件有可以用作器。通过继承现有的 Qt 部件可以很容易的定制自己的部件。当然在极少数情况下,为了特殊的应用,有可能要重头开始创建自己的部件。部件是 QWidget,它的子类或者由这些子类创建的自定制类的实例。一个部件可以包含很多子部件。这些子部件显示在父部件的区域内。一个没有父部件的部件我们称之为顶层部件(如一个窗口),它一般会在桌面环境的任务栏上占据一个位置。任何部件都可以使顶层部件,任何部件都可以成为其他部件的子部件。布局管理器会自动的安排子部件在父部件区域中的位置,当然你也可以手动来安排。当父部件无效,隐藏或者被删除的时候,这些动作也会影响相应的子部件。标签,消息框,工具提示等可以使用不止一种的颜色,字体和语言。例如:通过使用 HTML 的子集,Qt 的文本部件就可以显示多语种的文本。图图 17-1 Qwidget 类层次的一个片段类层次的一个片段 3、信号和槽 Qt 的内部对象通讯使用信号和槽机制。信号和槽机制很容易理解和使用,并且被Qt Designer 支持。GUI 程序需要相应用户的动作。例如,当用户点击一个菜单项或者工具栏按钮的时候,程序就会执行相应的代码。一般情况下,我们希望任何类型的对象之间可以互相通讯。编程人员需要把相应的事件和代码联系起来。以前的工具包使用了一种回调机制,这种机制不是类型安全的,它不够强壮并且不是面向对象的。Trolltech 提出了“信号和槽”的解决方案。它是一种强大的内部对象通讯机制,可以完全代替以前工具包使用的回调和 Message Maps 机制。信号和槽非常灵活,完全面向对象并且是使用 C+来实现的。使用以前的回调机制,为了把一个按钮和一段执行代码联系起来,则必须给这个按钮传递一个函数指针。这样,当这个按钮被按下时,这个函数就会被调用。但是以前的工具包并不保证传给这个函数的参数类型是正确的,这样就很容易发生问题。此外,回调机制把一个 GUI 元素和一个函数紧密的联系在一起,这样就很难独立的开发一个类。图图 17-2 信号和槽连接的抽象图信号和槽连接的抽象图 Qt 的信号和槽却不同,当一个事件发生时,Qt 部件会发送一个信号。例如,当一个按钮被按下的时候,它就可能发送clicked信号。编程人员可以创建一个函数(槽)并调用 connect()函数把这个槽和信号联系起来。Qt 的信号和槽机制并不要求一个类知道另一个类的信息,因此我们可以开发出高度可重用的类。信号和槽是类型安全的,当类型不匹配的时候,它会给出警告。例子,假设把退出按钮的 clicked()信号和程序的 quit()槽联系在一起,那么当用户点击退出按钮将终止程序。代码可能如下:connect(button,SIGNAL(clicked(),qApp,SLOT(quit();信号和槽之间的联系可以在程序运行期间动态的添加和删除。信号和槽的实现扩展了 C+语法并且充分利用了 C+的面向对象的性质。信号和槽是类型安全的,可以重载或者重新实现,而且它们在类中可以定义为公有的,保护的或者私有的。4、GUI 程序 用 Qt 可以既快速又容易的创建 GUI 程序,你可以手工编写代码也可以通过视觉化的编程工具 Qt Designer 来生成。Qt 提供了创建 GUI 程序所需的所有类和函数。你可以使用 Qt 来创建带有菜单,工具栏和状态栏的基于视窗的程序,也可以创建带有按钮和标签的基于对话框的程序。Qt支持单文档界面(SDI)和多文档界面(MDI)。此外,Qt 也支持拖放和剪贴。工具栏可以在其范围内任意移动,拖放到其他地方或者处于飘浮状态。这些功能是内建的,你不需要添加额外的代码。当然,如果需要你可以改变它的性质。Qt 简化了程序的设计。例如:如果菜单项,工具按钮和快捷键执行同一个动作,那么这个动作的实现代码可以只要一份。Qt 还提供了消息框和一系列的标准对话框来与用户交互。如:选择文件,文件夹,字体和颜色等。实际上,你有可能只是简单的使用 Qt 的一个语句就可以实现这个功能。六、实验说明六、实验说明 本实验通过一个简单基本的例子来阐述 Qt 应用程序的基本结构。实验 Hello world 程序中将显示有一段飘动的彩色的“Hello World”文字。实验代码如下:?hello.pro 文件内容如下:/*hello.pro*/TEMPLATE=app CONFIG =qt warn_on release HEADERS=hello.h SOURCES=hello.cpp main.cpp TARGET =hello?main.cpp 文件代码如下:/*main.cpp*/#include hello.h#include /*The program starts here.It parses the command line and builds a message string to be displayed by the Hello widget.*/int main(int argc,char*argv)QApplication a(argc,argv);QString s;for(int i=1;iargc;i+)s+=argvi;if(iargc-1)s+=;if(s.isEmpty()s=Hello,World;Hello h(s);h.setCaption(Qt says hello);QObject:connect(&h,SIGNAL(clicked(),&a,SLOT(quit();h.setFont(QFont(times,32,QFont:Bold);/default font h.setBackgroundColor(Qt:white);/default bg color a.setMainWidget(&h);h.show();return a.exec();?hello.h 头文件代码如下:/*hello.h*/#ifndef HELLO_H#define HELLO_H#include class Hello:public QWidget Q_OBJECT public:Hello(const char*text,QWidget*parent=0,const char*name=0);signals:void clicked();protected:void mouseReleaseEvent(QMouseEvent*);void paintEvent(QPaintEvent*);private slots:void animate();private:QString t;int b;#endif?hello.cpp 文件代码如下:/*hello.h*/#include hello.h#include#include#include#include /*Constructs a Hello widget.Starts a 40 ms animation timer.*/Hello:Hello(const char*text,QWidget*parent,const char*name):QWidget(parent,name),t(text),b(0)QTimer*timer=new QTimer(this);connect(timer,SIGNAL(timeout(),SLOT(animate();timer-start(40);resize(260,130);/*This private slot is called each time the timer fires.*/void Hello:animate()b=(b+1)&15;repaint(FALSE);/*Handles mouse button release events for the Hello widget.We emit the clicked()signal when the mouse is released inside the widget.*/void Hello:mouseReleaseEvent(QMouseEvent*e)if(rect().contains(e-pos()emit clicked();/*Handles paint events for the Hello widget.Flicker-free update.The text is first drawn in the pixmap and the pixmap is then blted to the screen.*/void Hello:paintEvent(QPaintEvent*)static int sin_tbl16=0,38,71,92,100,92,71,38,0,-38,-71,-92,-100,-92,-71,-38;if(t.isEmpty()return;/1:Compute some sizes,positions etc.QFontMetrics fm=fontMetrics();int w=fm.width(t)+20;int h=fm.height()*2;int pmx=width()/2-w/2;int pmy=height()/2-h/2;/2:Create the pixmap and fill it with the widgets background QPixmap pm(w,h);pm.fill(this,pmx,pmy);/3:Paint the pixmap.Cool wave effect QPainter p;int x=10;int y=h/2+fm.descent();int i=0;p.begin(&pm);p.setFont(font();while(!ti.isNull()int i16=(b+i)&15;p.setPen(QColor(15-i16)*16,255,255,QColor:Hsv);p.drawText(x,y-sin_tbli16*h/800,t.mid(i,1),1);x+=fm.width(ti);i+;p.end();/4:Copy the pixmap to the Hello widget bitBlt(this,pmx,pmy,&pm);实验代码说明:1)头文件 在头文件中,一般包含了程序所需要的数据结构、常量、或者定义好的函数,如果在应用程序中的开始加入头文件,就可以方便的使用它定义好的一切。一般在应用程序中,还会包含此程序自己定义的头文件。如在 main.cpp 中包含的头文件有:#include hello.h#include 在 hello.h 中包含有头文件有:#include 在 hello.cpp 中包含的头文件有:#include hello.h#include#include#include#include 其中是所有 Qt 应用程序所必须包含的类,它创建了应用程序的对象,来管理整个应用程序,包括管理整个应用程序范围内的各种资源,例如默认得字体和光标等。hello.h 是程序自己的头文件,它定义了自己的 hello 组件。程序中还用到了 QpushButton按钮,它在中有定义。由此可知,一个 Qt 应用程序一般包含三种头文件:?必要的头文件,一般包含在 main.cpp 文件中。?此应用程序自定义的组件的头文件,hello.h,一般在 hello.h 文件中定义,包含在 main.cpp 文件和 hello.cpp 文件中。?此应用程序用到的 Qt 的各类组件的头文件,如和等,一般包含在 hello.cpp 文件或者需要的地方。2)程序中的初始化 通常程序包含的主函数为:int main(int argc,char*argv)main()函数是程序的入口.在使用Qt时,main()只是在把控制交给Qt库之前做一些初始化的工作。argc 是命令行参数的数目,argv 是命令参数的数组.这并不是 Qt 特有的,而是 C+的特点,但是,Qt 需要处理这些参数。QApplication a(argc,argv);a 是本程序的 QApplication 对象,它被创建的目的是处理一些命令参数。注意,所有能被 Qt 识别的参数会被从 argv 中除去(argc 也会相应的减小为留下的参数的数目)。注意:在使用任何 Qt 窗口系统之前,QApplication 对象必须被创建。3)创建组件 Qt 中,在建立 Qapplication 对象之后,创建组件非常方便,只要建立所需要的组件类的对象即可。s=”Hello World”;Hello h(s);H 组件被选择为应用程序的主组件。4)设置组件的几何特性 在将显示组件之前,最好设置好组件的资源,如文字、大小、背景色、字体等,可以直接调用 Qwidght 类的成员函数,也可以重载它的成员函数。如:h.setCaption(Qt says hello);QObject:connect(&h,SIGNAL(clicked(),&a,SLOT(quit();h.setFont(QFont(times,32,QFont:Bold);/default font h.setBackgroundColor(Qt:white);/default bg color 5)Signal 和 Slot Qt 的特征就是采用了 Signal 和 Slot 机制,这里最重要函数 connect(),它连接发送端的信号至接收端的成员。当指定信号和成员函数时,用 SIGNAL()和 SLOT()宏。此实验中用到了 2 次此函数,一次连接单击信号 clicked()和退出 SLOT quit(),当运行这个程序时,单击组件时,将退出应用程序。QObject:connect(&h,SIGNAL(clicked(),&a,SLOT(quit();另一次是连接每次溢出时间,就飘动文字,当运行程序时,会看到飘动的文字。connect(timer,SIGNAL(timeout(),SLOT(animate();6)实现组件和进入主循环 在创建了一个组件后,它总是不可见的,必须调用 show()函数使其可见。hello.show();此函数用来显示组件和它的子组件。创建组件并显示后,程序将调用 exec()进入主事件循环体,它是 Qapplication 的成员函数。return a.exec();这里就是 main()把控制权交给 Qt 的地方。7)程序的编译和连接 编译时需要一个 Makefile 文件,在文件中编写了如何用头文件和源文件编译出目标文件的过程及一些设置。本实验中 Makefile 文件是通过 tmake 程序生成的。七、实验步骤七、实验步骤 1、按照实验说明,在 Linux 操作系统目录下新建一个文件夹,如 hello,在此工程文件夹下建立工程文件,hello.pro,main.cpp,hello.h,hello.cpp。2、在实验源程序完成后,使用 tmake 可执行程序生成 Makefile 文件,这是 hello.pro文件是必须得,使用如下命令:tmake-o Makefile hello.pro 这时,在文件目录下将生成 Makefile 文件,由于实验室编译 ARM 平台的程序 hello,因此首先必须指定 tmake.conf 文件的路径,这个文件指定了所用的编译器设置,所以环境变量的设置是必须得,否则将会可能无法生成我们所需要的 Makefile 文件或者无法指定正确的交叉编译器。环境变量的参考设置如下:export TMAKEDIR=$PWD export TMAKEPATH=$PWD/tmake-1.13/lib/qws/linux-arm-g+export PATH=$TMAKEDIR/bin:$PATH 3、在上一步中,我们已经生成了 Makefile 文件了,这样,我们就可以进行编译了,首先确定 Makefile 中的编译器使用的是正确的交叉编译器的路径。如果编译时报错交叉编译器命令错误,则必须修改 Makefile 文件中的交叉编译器,实验参考设置如下:CC=/usr/local/arm/2.95.3/bin/arm-linux-gcc CXX=/usr/local/arm/2.95.3/bin/arm-linux-g+LINK=/usr/local/arm/2.95.3/bin/arm-linux-gcc 如果编译中报错无法找到相关共享库文件,比如 libjpeg.so.62 库等,则首先确保在QT 移植的实验中已经正确的交叉编译安装了 jpeg-6b 库文件的软件包。如果已经安装,但是仍然无法寻找到共享库文件的路径,则需修改 Makefile 文件中的相关的库的变量值,将库文件添加进来,比如参考设置如下:SUBLIBS=-L/usr/local/arm/2.95.3/arm-linux/lib ljpeg 可能根据安装情况的不同所包含的库文件的路径也可能不同。如果此工程能顺利被编译,则会在工程文件目录下生成 hello 文件了,这个文件就是ARM 平台的可执行程序。4、生成 hello 文件后,下载到实验系统中观察程序的执行结果。在/DM2410/root/bin 目录下新建立一个 qtopia 的文本文件,其内容如下:#!Bin/sh export set HOME=/root export set QTDIR=/opt/qt export set QPEDIR=/opt/qpe export set QWS_KEYBOARD=USB:/dev/input/event0 export set QWS_MOUSE_PROTO=USB:/dev/input/mouse0 export set PATH=$QPEDIR/bin:$PATH export set LD_LIBRARY_PATH=$QTDIR/lib:$QPEDIR/lib$QPEDIR/bin/hello qws 然后将生成 hello 文件拷贝到/DM2410/root/opt/QPE/bin 目录下,通过网线将实验系统通过 NFS 服务器挂接上 PC 主机,如果已经连接上了 PC 机的 NFS 服务器,则执行:#qtopia 此时将会观察到实验结果输出。实验系统液晶显示屏上将会显示一个有 Hello World字符串的对话框界面。