Qt 嵌入式图形开发入门篇.docx
《Qt 嵌入式图形开发入门篇.docx》由会员分享,可在线阅读,更多相关《Qt 嵌入式图形开发入门篇.docx(21页珍藏版)》请在冰豆网上搜索。
Qt嵌入式图形开发入门篇
Qt嵌入式图形开发(入门篇)
一、Qt/Embedded开发环境的安装
一般来说,居于Qt/Embedded开发的应用程序最终会发布到安装有嵌入式Linux操作系统
的小型设备上,所以使用装有Linux操作系统的PC机或者工作站来完成Qt/Embedded开发当然
是最理想的环境,尽管Qt/Embedded也可以安装在Unix和Windows系统上。
下面我们将介绍如何在一台装有Linux操作系统的机器上建立Qt/Embedded开发环境。
首先,您需要拥有三个软件安装包:
tmake工具安装包,Qt/Embedded安装包,Qt的X11
版的安装包。
由于上述这些软件安装包有许多不同的版本,您要注意由于版本的不同导致这些软件在
使用时可能造成的冲突,为此我们将告诉您一些基本的安装原则:
当您选择或下载了
Qt/Embedded的某个版本的安装包之后,您下一步要选择安装的QtforX11的安装包的版本
必须比您最先下载的Qt/Embedded的版本要旧,这是因为QtforX11的安装包的两个工具uic
和designer产生的源文件会和Qt/Embedded的库一起被编译链接,本着“向前兼容”的原则,
QtforX11的版本应比Qt/Embedded的版本旧。
我们将以下面所列版本的安装包,一步一步介绍Qt/Embedded开发环境建立的过程(这
些软件可以免费从trolltech的WEB或FTP服务器上下载),
◆tmake1.11或更高版本;(生成Qt/Embedded应用工程的Makefile文件)
◆Qt/Embedded2.3.7(Qt/Embedded安装包)
◆Qt2.3.2forX11;(Qt的X11版的安装包,它将产生x11开发环境所需要的两个工具)
1、安装tmake
在Linux命令模式下运行以下命令:
tarxfztmake-1.11.tar.gz
exportTMAKEDIR=$PWD/tmake-1.11
exportTMAKEPATH=$TMAKEDIR/lib/qws/linux-x86-g++
exportPATH=$TMAKEDIR/bin:
$PATH
2.安装Qt/Embedded2.3.7
在Linux命令模式下运行以下命令:
tarxfzqt-embedded-2.3.7.tar.gz
cdqt-2.3.7
exportQTDIR=$PWD
exportQTEDIR=$QTDIR
exportPATH=$QTDIR/bin:
$PATH
exportLD_LIBRARY_PATH=$QTDIR/lib:
$LD_LIBRARY_PATH
./configure-qconfig-qvfb-depths4,8,16,32
makesub-src
cd..
上述命令./configure-qconfig-qvfb-depths4,8,16,32指定Qt嵌入式开发包生
成虚拟缓冲帧工具qvfb,并支持4,8,16,32位的显示颜色深度。
另外我们也可以在
configure的参数中添加-system-jpeg和gif,使Qt/Embedded平台能支持jpeg、gif
格式的图形。
上述命令makesub-src指定按精简方式编译开发包,也就是说有些Qt类未被编
译。
Qt嵌入式开发包有5种编译范围的选项,使用这些选项,可控制Qt生成的库文件的大
小,但是您的应用所使用到的一些Qt类将可能因此在Qt的库中找不到链接。
编译选项的具
体用法可运行./configure-help命令查看。
3.安装Qt/X112.3.2
在Linux命令模式下运行以下命令:
tarxfzqt-x11-2.3.2.tar.gz
cdqt-2.3.2
exportQTDIR=$PWD
exportPATH=$QTDIR/bin:
$PATH
exportLD_LIBRARY_PATH=$QTDIR/lib:
$LD_LIBRARY_PATH
./configure-no-opengl
make
make-Ctools/qvfb
mvtools/qvfb/qvfbbin
cpbin/uic$QTEDIR/bin
cd..
根据开发者本身的开发环境,也可以在configure的参数中添加别的参数,比如
-no-opengl或-no-xfs,可以键入./configure-help来获得一些帮助信息。
二、认识Qt/Embedded开发环境
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
2.1QT的支撑工具
Qt包含了许多支持嵌入式系统开发的工具,其中一些工具我们会在别的地方介绍。
有两个最实用的工具(除了上面我们提到的虚拟缓冲帧)是qmake和Qtdesigner(图
形设计器)。
qmake是一个为编译Qt/Embedded库和应用而提供的Makefile生成器。
它能够根据
一个工程文件(.pro)产生不同平台下的Makefile文件。
qmake支持跨平台开发和影子
生成(shadowbuilds),影子生成是指当工程的源代码共享给网络上的多台机器时,每
台机器编译链接这个工程的代码将在不同的子路径下完成,这样就不会覆盖别人的编译
链接生成的文件。
qmake还易于在不同的配置之间切换。
开发者可以使用Qt图形设计器可视化地设计对话框而不需编写一行代码。
使用Qt
图形设计器的布局管理可以生成具有平滑改变尺寸的对话框,qmake和Qt图形设计器是
完全集成在一起的。
2.2信号与插槽
信号与插槽机制提供了对象间的通信机制,它易于理解和使用,并完全被Qt图形
设计器所支持。
图形用户接口的应用需要对用户的动作做出响应。
例如,当用户点击了一个菜单项
或是工具栏的按钮时,应用程序会执行某些代码。
大部分情况下,我们希望不同类型的
对象之间能够进行通信。
程序员必须把事件和相关代码联系起来,这样才能对事件做出
响应。
以前的工具开发包使用的事件响应机制是易崩溃的,不够健壮的,同时也不是面
向对象的。
Trolltech已经创立了一种新的机制,叫做“信号与插槽”。
信号与插槽是一
种强有力的对象间通信机制,它完全可以取代原始的回调和消息映射机制;信号与插槽
是迅速的,类型安全的,健壮的,完全面向对象并用C++来实现的一种机制。
在以前,当我们使用回调函数机制来把某段响应代码和一个按钮的动作相关联时,
我们通常把那段响应代码写成一个函数,然后把这个函数的地址指针传给按钮,当那个
按钮被按下时,这个函数就会被执行。
对于这种方式,以前的开发包不能够确保回调函
数被执行时所传递进来的函数参数就是正确的类型,因此容易造成进程崩溃,另外一个
问题是,回调这种方式紧紧的绑定了图形用户接口的功能元素,因而很难把开发进行独
立的分类。
Qt的信号与插槽机制是不同的。
Qt的窗口在事件发生后会激发信号。
例如一个按钮
被点击时会激发一个“clicked”信号。
程序员通过建立一个函数(称作一个插槽),
对象1
图一一些信号与插槽连接的抽象图
然后调用connect()函数把这个插槽和一个信号连接起来,这样就完成了一个事件和响
应代码的连接。
信号与插槽机制并不要求类之间互相知道细节,这样就可以相对容易的
开发出代码可高重用的类。
信号与插槽机制是类型安全的,它以警告的方式报告类型错
误,而不会使系统产生崩溃。
例如,如果一个退出按钮的clicked()信号被连接到了一个应用的退出函数-
信号1
信号2
插槽1
插槽2
插槽1
插槽2
插槽3
插槽1
信号1
信号1
对象3
对象4
connect(对象1,信号1,对象2,插槽1)
connect(对象1,信号1,对象2,插槽2)
connect(对象1,信号2,对象4,插槽1)
connect(对象3,信号1,对象4,插槽3)
对象2
quit()插槽。
那么一个用户点击退出键将使应用程序终止运行。
上述的连接过程用代
码写出来就是这样
connect(button,SIGNAL(clicked()),qApp,SLOT(quit()));
我们可以在Qt应用程序的执行过程中增加或是减少信号与插槽的连接。
信号与插槽的实现扩展了C++的语法,同时也完全利用了C++面向对象的特征。
信号
与插槽可以被重载或者重新实现,它们可以定义为类的公有,私有或是保护成员。
2.2.1信号与插槽的例子
如果一个类要使用信号与插槽机制,它就必须是从QObject或者QObject的子类继承,而
且在类的定义中必须加上Q_OBJECT宏。
信号被定义在类的信号部分,而插槽则定义在public
slots,protectedslots或者privateslots部分。
下面定义一个使用到信号与插槽机制的类。
classBankAccount:
publicQObject
{
Q_OBJECT
public:
BankAccount(){curBalance=0;}
intbalance()const{returncurBalance;}
publicslots:
voidsetBalance(intnewBalance);
signals:
voidbalanceChanged(intnewBalance);
private:
intcurBalance;
};
和大部分的C++的类一样,BankAccount类有一个构造函数,还有一个取值的函数
balance(),一个设置值的函数setBalance(intnewBalance)。
这个类有一个信号balanceChanged(),这个信号声明了它在BankAccount类的成员
curBalance的值被改变时产生。
信号不需要被实现,当信号被激发时,和该信号连接的插槽
将被执行。
上面用来设置值的函数setBalance(intnewBalance)定义在类的“publicslots”
部分,因此它是一个插槽。
插槽是一个需要实现的标准的成员函数,它可以像其它函数一样
被调用,也可以和信号相连接。
下面就是该插槽函数setBalance(intnewBalance)的实现代码:
voidBankAccount:
:
setBalance(intnewBalance)
{
if(newBalance!
=curBalance)
{
curBalance=newBalance;
emitbalanceChanged(curBalance);
}
}
其中的一段代码
emitbalanceChanged(curBalance);
它的作用是当curBalance的值被改变后,将新的curBalance的值作为参数去激活
balanceChanged()信号。
对于关键词“emit”,它和信号、插槽一样是由Qt提供的,这
些关键词都会被c++的预处理机制转换为c++代码。
一个对象的信号可以被多个不同的插槽连接,而多个信号也可以被连接到相同的插槽。
当信号和插槽被连接起来时,应当确保它们的参数类型是相同的,如果插槽的参数个数小于
和它连接在一起的信号的参数个数,那么从信号传递插槽的多余的参数将被忽略。
2.2.2元对象编译器
信号与插槽机制是以纯C++代码来实现的,实现的过程使用到了Qt开发工具包提供的
预处理器和元对象编译器(moc)。
moc读取应用程序的头文件,并产生支持信号与插槽的必要的代码。
开发者没必要编辑
或是浏览这些自动产生的代码,当有需要时,qmake生成的Makefile文件里会显式的包含
了运行moc的规则。
除了可以处理信号与插槽机制之外,moc还支持翻译机制,属性系统和运行时的信息。
2.3窗体
Qt拥有丰富的满足不同需求的窗体(按钮,滚动条等等),Qt的窗体使用起来很灵
活,为了满足特别的要求,它很容易就可以被子类化。
窗体是Qwidget类或它子类的实例,客户自己的窗体类需要从Qwidget它的子类继
承。
图二摘录的Qwidget类的继承图
一个窗体可以包含任意数量的子窗体,子窗体可以显示在父窗体的客户区,一个没
父窗体的窗体我们称之为顶级窗体(一个“窗口”),一个窗体通常有一个边框和标题栏
作为装饰。
Qt并未对一个窗体有什么限制,任何类型的窗体可以是顶级窗体,任何类型
的窗体可以是别的窗体的子窗体。
在父窗体显示区域的子窗体的位置可以通过布局管理
自动的进行设置,也可以人为的指定。
当父窗体无效,隐藏或被删除后,它的子窗体都
会进行同样的动作。
标签,消息框,工具栏等等等,并未被限制使用什么颜色,字体和语言。
Qt的文本
呈现窗体可以使用HTML子集显示一个多语言的宽文本。
2.3.1一个Hello的例子
下面是一个显示“HelloQt/Embedded!
”的程序的完整的源代码:
图三HelloQt/Embedded
#include
#include
intmain(intargc,char**argv)
{
QApplicationapp(argc,argv);
QLabel*hello=newQLabel("Hello"
"Qt/Embedded!
",0);
app.setMainWidget(hello);
hello->show();
returnapp.exec();
}
2.3.2通用窗体
下面是一些主要的Qt窗体的截屏图,这些窗体使用了窗口样式。
图四使用了QHBox进行排列一个标签和一个按钮
图五使用了QbuttonGroup的两个单选框和两个复选框
图六使用了QgroupBox进行排列的的日期类QDateTimeEdit,一个行编辑框类QLineEdit,一个文本编辑类
QTextEdit和一个组合框类QComboBox
图七以QGrid排列的一个QDial,一个QProgressBar,一个QSpinBox,一个QScrollBar,一个QLCDNumber
和一个QSlider
图八以QGrid排列的一个QIconView,一个QListView,一个QListBox和一个QTable
有些时候在进行字符输入时,我们希望输入的字符满足了某种规则才能使输入被确认。
Qt提供了解决的办法,例如QComboBox,QLineEdit和QspinBox的字符输入可以通过
Qvalidator的子类来进行约束和有效性检查。
通过继承QScrollView,QTable,QListView,QTextEdit和其它窗体就能够显示大量
的数据,并且自动的拥有了一个滚动条。
许多Qt创建的窗体能够显示图像,例如按钮,标签,菜单项等等。
Qimage类支持几种图
形格式的输入、输出和操作,它目前支持的图形格式有BMP,GIF*,JPEG,MNG,PNG,PNM,XBM
和XPM。
2.3.3画布
QCanvas类提供了一个高级的平面图形编程接口,它可以处理大量的像线条、矩形、椭
圆、文本、位图、动画等这些画布项,画布项可以较容易的做成交互式的(例如做成支持用
户移动的)。
画布项是QcanvasItem子类的实例,它们比窗体类Qwidget更显得轻量级,它们能够被快
速的移动,隐藏和显示。
Qcanvas可以更有效的支持冲突检测,它能够列出一个指定区域里
面的所有的画布项。
QcanvasItem可以被子类化,从而可以提供更多的客户画布项类型,或
者扩展已有的画布项的功能。
Qcanvas对象是由QcanvasView进行绘制的,QcanvasView对象可以以不同的译文、比例、
旋转角度,剪切方式去显示同一个画布。
Qcanvas对象是理想的数据表现方式,它已经被消费者用于绘制地图和显示网络拓扑结
构。
它也可用于制作快节奏的且有大量角色的平面游戏。
图九在Qtopia中用QCanvas实现的小行星游戏
2.3.4客户窗体
通过对Qwidget或者它的子类进行子类化,我们可以建立自己的客户窗体或者对话框。
下面
是一个完整的源代码例子,它示例了如何通过子类化窗体,绘制一个模拟的时钟。
AnalogClock窗体类是Qwidget的子类,它显示当前时间,并且可以自动地更新时间。
图十模拟钟窗体
在analogclock.h头文件中,AnalogClock以这样地形式定义:
:
#include
classAnalogClock:
publicQWidget
{
public:
AnalogClock(QWidget*parent=0,constchar*name=0);
protected:
virtualvoidtimerEvent(QTimerEvent*event);
virtualvoidpaintEvent(QPaintEvent*event);
};
AnalogClock类继承了Qwidget,它有一个典型的窗体类构造函数,这个函数有父窗口对象
指针和名字指针两个参数。
(如果设置了名字的话,测试和调试起来就会容易些)
timerEvent()函数是从QObject(Qwidget的父类)对象继承而来的,这个函数会被系统定
期调用。
paintEvent()函数是从QWidget继承而来的并且当窗体需要重画时这个函数就会
被调用。
timerEvent()和paintEvent()函数是“事件句柄”的两个例子。
应用对象以
重载父类对象的虚拟函数events(QEventobjects)的形式接收系统的事件。
大约有超过50
个的系统事件是较常用的,例如MouseButtonPress,MouseButtonRelease,
KeyPress,KeyRelease,Paint,Resize和Close.对象可以对发给它们的事件做出响应或
者筛选一些事件后再发送给别的对象。
analogclock.cpp文件是定义在analogclock.h中的函数的实现源文件
#include
#include
#include"analogclock.h"
AnalogClock:
:
AnalogClock(QWidget*parent,constchar*name)
:
QWidget(parent,name)
{
startTimer(12000);
resize(100,100);
}
voidAnalogClock:
:
timerEvent(QTimerEvent*)
{
update();
}
voidAnalogClock:
:
paintEvent(QPaintEvent*)
{
QCOORDhourHand[8]={2,0,0,2,-2,0,0,-25};
QCOORDminuteHand[8]={1,0,0,1,-1,0,0,-40};
QTimetime=QTime:
:
currentTime();
QPainterpainter(this);
painter.setWindow(-50,-50,100,100);
painter.setBrush(black);
for(inti=0;i<12;i++)
{
painter.drawLine(44,0,46,0);
painter.rotate(30);
}
painter.save();
painter.rotate(30*(time.hour()%12)+time.minute()/2);
painter.drawConvexPolygon(QPointArray(4,hourHand));
painter.restore();
painter.save();
painter.rotate(6*time.minute());
painter.drawConvexPolygon(QPointArray(4,minuteHand));
painter.restore();
}
构造函数设置窗口的尺寸大小为100x100,并且告诉系统每隔12秒调用一次
timerEvent()函数,从而对模拟钟的窗体进行刷新。
在timerEvent()函数中,通过调用QWidget的函数update()就可以告诉Qt,窗体需要
立即重画,紧接着Qt就会产生一个绘制事件并且调用paintEvent()函数。
在paintEvent()函数中,一个Qpainter对象用于在窗体上绘制12个刻度以及分针,时针。
Qpainter类提供了一种统一的方式用于绘制窗体,位图,矢量图等,它提供了绘制点,线,
椭圆,多边形,弧,贝塞尔曲线等功能,一个Qpainter的坐标系可以被转变,缩放,旋转,
和剪切,这