1、Qt嵌入式图形开发入门篇Qt 嵌入式图形开发(入门篇)作者:深圳市优龙科技有限公司时间:2004/6/7一、Qt/Embedded 开发环境的安装一般来说,居于Qt/Embedded开发的应用程序最终会发布到安装有嵌入式Linux操作系统的小型设备上,所以使用装有Linux操作系统的PC机或者工作站来完成Qt/Embedded开发当然是最理想的环境,尽管Qt/Embedded也可以安装在Unix和Windows系统上。下面我们将介绍如何在一台装有Linux操作系统的机器上建立Qt/Embedded开发环境。 首先,您需要拥有三个软件安装包:tmake工具安装包,Qt/Embedded 安装包,
2、Qt的X11版的安装包。 由于上述这些软件安装包有许多不同的版本,您要注意由于版本的不同导致这些软件在使用时可能造成的冲突,为此我们将告诉您一些基本的安装原则:当您选择或下载了Qt/Embedded 的某个版本的安装包之后,您下一步要选择安装的Qt for X11的安装包的版本必须比您最先下载的Qt/Embedded 的版本要旧,这是因为Qtfor X11的安装包的两个工具uic和designer产生的源文件会和Qt/Embedded的库一起被编译链接,本着“向前兼容”的原则,Qt for X11的版本应比Qt/Embedded的版本旧。我们将以下面所列版本的安装包,一步一步介绍Qt/Embe
3、dded开发环境建立的过程(这些软件可以免费从trolltech的WEB或FTP服务器上下载), tmake 1.11 或更高版本;(生成Qt/Embedded应用工程的Makefile文件) Qt/Embedded 2.3.7 (Qt/Embedded 安装包) Qt 2.3.2 for X11; (Qt的X11版的安装包, 它将产生x11开发环境所需要的两个工具)1、安装tmake 在Linux命令模式下运行以下命令:2. 安装Qt/Embedded 2.3.7在Linux命令模式下运行以下命令:上述命令 ./configure -qconfig -qvfb -depths 4,8,16,
4、32 指定Qt 嵌入式开发包生成虚拟缓冲帧工具qvfb,并支持4,8,16,32 位的显示颜色深度。另外我们也可以在configure 的参数中添加systemjpeg和gif,使Qt/Embedded平台能支持jpeg、gif 格式的图形。 上述命令 make sub-src 指定按精简方式编译开发包,也就是说有些Qt 类未被编译。Qt嵌入式开发包有5种编译范围的选项,使用这些选项,可控制Qt 生成的库文件的大小,但是您的应用所使用到的一些Qt 类将可能因此在Qt的库中找不到链接。编译选项的具体用法可运行./configure help 命令查看。3. 安装Qt/X11 2.3.2在Linu
5、x命令模式下运行以下命令:根据开发者本身的开发环境,也可以在configure 的参数中添加别的参数,比如-no-opengl 或-no-xfs,可以键入./configure -help 来获得一些帮助信息。二、认识Qt/Embedded 开发环境 Qt/Embedded 的开发环境可以取代那些我们熟知的UNIX 和WINDOWS 开发工具。它提供了几个跨平台的工具使得开发变得迅速和方便,尤其是它的图形设计器。Unix 下的开发者可以在PC 机或者工作站使用虚拟缓冲帧,从而可以仿真一个和嵌入式设备的显示终端大小,象素相同的显示环境。嵌入式设备的应用可以在安装了一个跨平台开发工具链的不同的平台
6、上编译。最通常的做法是在一个UNIX系统上安装跨平台的带有libc库的GNU c+编译器和二进制工具。在开发的许多阶段,一个可替代的做法是使用Qt的桌面版本,例如Qt/X11或是Qt/Windows来进行开发。这样开发人员就可以使用他们熟悉的开发环境,例如微软的Visual C+ 或者 Borland C+;在UNIX操作系统下,许多环境也是可用的,例如Kdevelop,它也支持交互式开发。如果Qt/Embedded 的应用是在UNIX 平台下开发的话,那么它就可以在开发的机器上以一个独立的控制台或者虚拟缓冲帧的方式来运行,对于后者来说,其实是有一个X11的应用程序虚拟了一个缓冲帧。通过指定显
7、示设备的宽度,高度和颜色深度,虚拟出来的缓冲帧将和物理的显示设备在每个像素上保持一致。这样每次调试应用时开发人员就不用总是刷新嵌入式设备的FLASH存储空间,从而加速了应用的编译、链接和运行周期。运行Qt 的虚拟缓冲帧工具的方法是:在Linux的图形模式下运行命令:当Qt 嵌入式的应用程序要把显示结果输出到虚拟缓冲帧时,我们在命令行运行这个程序时,在程序名后加上-qws 的选项。例如:$ hello -qws 21 QT 的支撑工具 Qt 包含了许多支持嵌入式系统开发的工具,其中一些工具我们会在别的地方介绍。有两个最实用的工具(除了上面我们提到的虚拟缓冲帧)是qmake 和Qt designe
8、r(图形设计器)。 qmake 是一个为编译Qt/Embedded库和应用而提供的Makefile 生成器。它能够根据一个工程文件(.pro)产生不同平台下的Makefile文件。qmake支持跨平台开发和影子生成(shadow builds),影子生成是指当工程的源代码共享给网络上的多台机器时,每台机器编译链接这个工程的代码将在不同的子路径下完成,这样就不会覆盖别人的编译链接生成的文件。qmake还易于在不同的配置之间切换。开发者可以使用Qt 图形设计器可视化地设计对话框而不需编写一行代码。使用Qt图形设计器的布局管理可以生成具有平滑改变尺寸的对话框,qmake 和Qt 图形设计器是完全集成
9、在一起的。22 信号与插槽 信号与插槽机制提供了对象间的通信机制,它易于理解和使用,并完全被Qt 图形设计器所支持。 图形用户接口的应用需要对用户的动作做出响应。例如,当用户点击了一个菜单项或是工具栏的按钮时,应用程序会执行某些代码。大部分情况下,我们希望不同类型的对象之间能够进行通信。程序员必须把事件和相关代码联系起来,这样才能对事件做出响应。以前的工具开发包使用的事件响应机制是易崩溃的,不够健壮的,同时也不是面向对象的。Trolltech 已经创立了一种新的机制,叫做“信号与插槽”。信号与插槽是一种强有力的对象间通信机制,它完全可以取代原始的回调和消息映射机制;信号与插槽 是迅速的,类型安
10、全的,健壮的,完全面向对象并用C+来实现的一种机制。在以前,当我们使用回调函数机制来把某段响应代码和一个按钮的动作相关联时,我们通常把那段响应代码写成一个函数,然后把这个函数的地址指针传给按钮,当那个按钮被按下时,这个函数就会被执行。对于这种方式,以前的开发包不能够确保回调函数被执行时所传递进来的函数参数就是正确的类型,因此容易造成进程崩溃,另外一个问题是,回调这种方式紧紧的绑定了图形用户接口的功能元素,因而很难把开发进行独立的分类。 Qt的信号与插槽机制是不同的。Qt的窗口在事件发生后会激发信号。例如一个按钮被点击时会激发一个“clicked”信号。程序员通过建立一个函数(称作一个插槽),
11、对象1connect( 对象1, 信号1, 对象2, 插槽1 ) 信号1 connect( 对象1, 信号1, 对象2, 插槽2 ) 信号2 对象2信号1 插槽1 插槽2 对象3 信号1 connect( 对象1, 信号2, 对象4, 插槽1 )对象4 插槽1 插槽1 插槽2 插槽3 connect( 对象3, 信号1, 对象4, 插槽3 ) 图一 一些信号与插槽连接的抽象图然后调用connect() 函数把这个插槽和一个信号连接起来,这样就完成了一个事件和响应代码的连接。信号与插槽机制并不要求类之间互相知道细节,这样就可以相对容易的开发出代码可高重用的类。信号与插槽机制是类型安全的,它以警告
12、的方式报告类型错误,而不会使系统产生崩溃。例如,如果一个退出按钮的clicked() 信号被连接到了一个应用的退出函数quit() 插槽。那么一个用户点击退出键将使应用程序终止运行。上述的连接过程用代码写出来就是这样 connect( button, SIGNAL(clicked(), qApp, SLOT(quit() ); 我们可以在Qt应用程序的执行过程中增加或是减少信号与插槽的连接。信号与插槽的实现扩展了C+的语法,同时也完全利用了C+面向对象的特征。信号与插槽可以被重载或者重新实现,它们可以定义为类的公有,私有或是保护成员。221 信号与插槽的例子如果一个类要使用信号与插槽机制,它就
13、必须是从QObject或者QObject的子类继承,而且在类的定义中必须加上Q_OBJECT宏。信号被定义在类的信号部分,而插槽则定义在publicslots, protected slots 或者 private slots 部分。下面定义一个使用到信号与插槽机制的类。class BankAccount : public QObject Q_OBJECT public: BankAccount() curBalance = 0; int balance() const return curBalance; public slots: void setBalance( int newBalan
14、ce ); signals: void balanceChanged( int newBalance ); private: int curBalance; ; 和大部分的C+的类一样,BankAccount类有一个构造函数,还有一个取值的函数balance(),一个设置值的函数setBalance( int newBalance )。这个类有一个信号balanceChanged(),这个信号声明了它在BankAccount类的成员curBalance的值被改变时产生。信号不需要被实现,当信号被激发时,和该信号连接的插槽将被执行。 上面用来设置值的函数setBalance( int newBa
15、lance )定义在类的“public slots”部分,因此它是一个插槽。插槽是一个需要实现的标准的成员函数,它可以像其它函数一样被调用,也可以和信号相连接。下面就是该插槽函数setBalance( int newBalance )的实现代码:void BankAccount:setBalance( int newBalance ) if ( newBalance != curBalance ) curBalance = newBalance; emit balanceChanged( curBalance ); 其中的一段代码 emit balanceChanged( curBalance
16、 ); 它的作用是当curBalance 的值被改变后,将新的curBalance 的值作为参数去激活balanceChanged()信号。对于关键词“emit”,它和信号、插槽一样是由Qt 提供的,这些关键词都会被c的预处理机制转换为c代码。一个对象的信号可以被多个不同的插槽连接,而多个信号也可以被连接到相同的插槽。当信号和插槽被连接起来时,应当确保它们的参数类型是相同的,如果插槽的参数个数小于和它连接在一起的信号的参数个数,那么从信号传递插槽的多余的参数将被忽略。222 元对象编译器信号与插槽机制是以纯C+代码来实现的,实现的过程使用到了Qt 开发工具包提供的预处理器和元对象编译器(moc
17、)。 moc 读取应用程序的头文件,并产生支持信号与插槽的必要的代码。开发者没必要编辑或是浏览这些自动产生的代码,当有需要时,qmake 生成的Makefile 文件里会显式的包含了运行moc的规则。除了可以处理信号与插槽机制之外,moc还支持翻译机制,属性系统和运行时的信息。23 窗体Qt 拥有丰富的满足不同需求的窗体(按钮,滚动条等等),Qt 的窗体使用起来很灵活,为了满足特别的要求,它很容易就可以被子类化。 窗体是Qwidget 类或它子类的实例,客户自己的窗体类需要从Qwidget 它的子类继承。图二 摘录的Qwidget 类的继承图 一个窗体可以包含任意数量的子窗体,子窗体可以显示在
18、父窗体的客户区,一个没父窗体的窗体我们称之为顶级窗体(一个“窗口”),一个窗体通常有一个边框和标题栏作为装饰。Qt并未对一个窗体有什么限制,任何类型的窗体可以是顶级窗体,任何类型的窗体可以是别的窗体的子窗体。在父窗体显示区域的子窗体的位置可以通过布局管理自动的进行设置,也可以人为的指定。当父窗体无效,隐藏或被删除后,它的子窗体都会进行同样的动作。标签,消息框,工具栏等等等,并未被限制使用什么颜色,字体和语言。Qt的文本呈现窗体可以使用HTML子集显示一个多语言的宽文本。231 一个Hello 的例子下面是一个显示“Hello Qt/Embedded!”的程序的完整的源代码: 图三 Hello
19、Qt/Embedded #include #include int main( int argc, char *argv ) QApplication app( argc, argv ); QLabel *hello = new QLabel( Hello Qt/Embedded!, 0 );app.setMainWidget( hello );hello-show();return app.exec(); 232 通用窗体 下面是一些主要的Qt 窗体的截屏图,这些窗体使用了窗口样式。图四 使用了QHBox进行排列一个标签和一个按钮图五 使用了QbuttonGroup的两个单选框和两个复选框图
20、六 使用了QgroupBox进行排列的的日期类QDateTimeEdit, 一个行编辑框类QLineEdit, 一个文本编辑类QTextEdit 和一个组合框类QComboBox图七 以QGrid排列的一个QDial, 一个QProgressBar, 一个QSpinBox, 一个QScrollBar,一个QLCDNumber和一个QSlider图八 以QGrid排列的一个QIconView, 一个 QListView, 一个 QListBox 和一个QTable有些时候在进行字符输入时,我们希望输入的字符满足了某种规则才能使输入被确认。Qt提供了解决的办法,例如QComboBox, QLine
21、Edit 和 QspinBox 的字符输入可以通过Qvalidator的子类来进行约束和有效性检查。 通过继承QScrollView ,QTable, QListView, QTextEdit 和其它窗体就能够显示大量的数据,并且自动的拥有了一个滚动条。许多Qt创建的窗体能够显示图像,例如按钮,标签,菜单项等等。Qimage类支持几种图形格式的输入、输出和操作,它目前支持的图形格式有BMP, GIF*, JPEG, MNG, PNG, PNM,XBM 和 XPM。 233 画布QCanvas 类提供了一个高级的平面图形编程接口,它可以处理大量的像线条、矩形、椭圆、文本、位图、动画等这些画布项,
22、画布项可以较容易的做成交互式的(例如做成支持用户移动的)。 画布项是QcanvasItem子类的实例,它们比窗体类Qwidget更显得轻量级,它们能够被快速的移动,隐藏和显示。Qcanvas可以更有效的支持冲突检测,它能够列出一个指定区域里面的所有的画布项。QcanvasItem可以被子类化,从而可以提供更多的客户画布项类型,或者扩展已有的画布项的功能。Qcanvas对象是由QcanvasView进行绘制的,QcanvasView对象可以以不同的译文、比例、旋转角度,剪切方式去显示同一个画布。Qcanvas 对象是理想的数据表现方式,它已经被消费者用于绘制地图和显示网络拓扑结构。它也可用于制作
23、快节奏的且有大量角色的平面游戏。 图九 在Qtopia 中用QCanvas实现的小行星游戏 234 客户窗体 通过对Qwidget或者它的子类进行子类化,我们可以建立自己的客户窗体或者对话框。下面是一个完整的源代码例子,它示例了如何通过子类化窗体,绘制一个模拟的时钟。AnalogClock 窗体类是Qwidget的子类,它显示当前时间,并且可以自动地更新时间。 图十 模拟钟窗体在 analogclock.h头文件中,AnalogClock 以这样地形式定义::#include class AnalogClock : public QWidget public: AnalogClock( QWi
24、dget *parent = 0, const char *name = 0 ); protected: virtual void timerEvent( QTimerEvent *event );virtual void paintEvent( QPaintEvent *event ); AnalogClock 类继承了Qwidget, 它有一个典型的窗体类构造函数,这个函数有父窗口对象指针和名字指针两个参数。(如果设置了名字的话,测试和调试起来就会容易些) timerEvent() 函数是从QObject (Qwidget的父类)对象继承而来的,这个函数会被系统定期调用。paintEven
25、t() 函数是从QWidget 继承而来的并且当窗体需要重画时这个函数就会被调用。 timerEvent() 和 paintEvent() 函数是“事件句柄”的两个例子。应用对象以 重载父类对象的虚拟函数events (QEvent objects) 的形式接收系统的事件。大约有超过50个的系统事件是较常用的,例如 MouseButtonPress, MouseButtonRelease, KeyPress, KeyRelease, Paint, Resize 和Close. 对象可以对发给它们的事件做出响应或者筛选一些事件后再发送给别的对象。analogclock.cpp 文件是定义在ana
26、logclock.h中的函数的实现源文件#include #include #include analogclock.hAnalogClock:AnalogClock( QWidget *parent, const char *name ) : QWidget( parent, name ) startTimer( 12000 ); resize( 100, 100 ); void AnalogClock:timerEvent( QTimerEvent * ) update(); void AnalogClock:paintEvent( QPaintEvent * ) QCOORD hourH
27、and8 = 2, 0, 0, 2, -2, 0, 0, -25 ; QCOORD minuteHand8 = 1, 0, 0, 1, -1, 0, 0, -40 ; QTime time = QTime:currentTime();QPainter painter( this );painter.setWindow( -50, -50, 100, 100 );painter.setBrush( black );for ( int i = 0; i 12; i+ ) painter.drawLine( 44, 0, 46, 0 );painter.rotate( 30 ); painter.s
28、ave();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(); 构造函数设置窗口的尺寸大小为100 x 100,并且告诉系统每
29、隔12秒调用一次timerEvent()函数,从而对模拟钟的窗体进行刷新。在 timerEvent()函数中, 通过调用QWidget的函数update()就可以告诉Qt,窗体需要立即重画,紧接着Qt就会产生一个绘制事件并且调用paintEvent()函数。在paintEvent()函数中,一个Qpainter对象用于在窗体上绘制12个刻度以及分针,时针。Qpainter类提供了一种统一的方式用于绘制窗体,位图,矢量图等,它提供了绘制点,线,椭圆,多边形,弧,贝塞尔曲线等功能,一个Qpainter的坐标系可以被转变,缩放,旋转,和剪切,这样对象就可以根据它在窗口或者窗体上的位置绘制出一个剪切的
30、视图。剪切可以使窗体绘制时减少闪烁。使用QPainter 的子类QdirectPainter可以锁定和直接访问帧缓冲区域。 文件 analogclock.h 和 analogclock.cpp 完全的定义和实现了 AnalogClock 客户窗体类,这个窗体是现在就可以使用的。 #include #include analogclock.h int main( int argc, char *argv ) QApplication app( argc, argv );AnalogClock *clock = new AnalogClock; app.setMainWidget( clock )
31、;clock-show();return app.exec(); 235 主窗口 QMainWindow类是为应用的主窗口提供一个摆放相关窗体的框架。一个主窗口包含了一组标准窗体的集合。主窗口的顶部包含一个菜单栏,它的下方放置着一个工具栏,工具栏可以移动到其它的停靠区域。主窗口允许停靠的位置有顶部,左边,右边和底部。工具栏可以被拖放到一个停靠的位置,从而形成一个浮动的工具面板。主窗口的下方,也就是在底部的停靠位置之下有一个状态栏。主窗口的中间区域可以包含其它的窗体。提示工具和“这是什么”帮助按钮以旁述的方式阐述了用户接口的使用方法。对于小屏幕的设备,使用Qt图形设计器定义的标准的Qwidget模板比使用主窗口类更好一些。典型的模板包含有菜单栏,工具栏,可能没有状态栏(在必要的情况下,可以用任务栏,标题栏来显示状态)236 菜单弹出式菜单QpopupMenu类以垂直列表的方式显示菜单项,它可以是单个
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1