首页资源分类嵌入式系统其他 > Qt嵌入式图形编程(入门加实战)

Qt嵌入式图形编程(入门加实战)

已有 445125个资源

下载专区

上传者其他资源

    文档信息举报收藏

    标    签:QT

    分    享:

    文档简介

    学习QT编程的一本资料

    文档预览

    本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 Qt 嵌入式图形开发(入门篇) 作者:深圳市优龙科技有限公司 时间:2004/6/7 一、Qt/Embedded 开发环境的安装 一般来说,居于Qt/Embedded开发的应用程序最终会发布到安装有嵌入式Linux操作系统 的小型设备上,所以使用装有Linux操作系统的PC机或者工作站来完成Qt/Embedded开发当然 是最理想的环境,尽管Qt/Embedded也可以安装在Unix和Windows系统上。 下面我们将介绍如何在一台装有Linux操作系统的机器上建立Qt/Embedded开发环境。 首先,您需要拥有三个软件安装包:tmake工具安装包,Qt/Embedded 安装包,Qt的X11 版的安装包。 由于上述这些软件安装包有许多不同的版本,您要注意由于版本的不同导致这些软件在 使用时可能造成的冲突,为此我们将告诉您一些基本的安装原则:当您选择或下载了 Qt/Embedded 的某个版本的安装包之后,您下一步要选择安装的Qt for X11的安装包的版本 必须比您最先下载的Qt/Embedded 的版本要旧,这是因为Qt for X11的安装包的两个工具uic 和designer产生的源文件会和Qt/Embedded的库一起被编译链接,本着“向前兼容”的原则, Qt for X11的版本应比Qt/Embedded的版本旧。 我们将以下面所列版本的安装包,一步一步介绍Qt/Embedded开发环境建立的过程(这 些软件可以免费从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 命令模式下运行以下命令: 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 2. 安装 Qt/Embedded 2.3.7 在 Linux 命令模式下运行以下命令: 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 tar xfz qt-embedded-2.3.7.tar.gz cd qt-2.3.7 export QTDIR=$PWD export QTEDIR=$QTDIR export PATH=$QTDIR/bin:$PATH export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH ./configure -qconfig -qvfb -depths 4,8,16,32 make sub-src cd .. 上述命令 ./configure -qconfig -qvfb -depths 4,8,16,32 指定 Qt 嵌入式开发包生 成虚拟缓冲帧工具 qvfb,并支持 4,8,16,32 位的显示颜色深度。另外我们也可以在 configure 的参数中添加-system-jpeg 和 gif,使 Qt/Embedded 平台能支持 jpeg、gif 格式的图形。 上述命令 make sub-src 指定按精简方式编译开发包,也就是说有些 Qt 类未被编 译。Qt 嵌入式开发包有 5 种编译范围的选项,使用这些选项,可控制 Qt 生成的库文件的大 小,但是您的应用所使用到的一些 Qt 类将可能因此在 Qt 的库中找不到链接。编译选项的具 体用法可运行./configure -help 命令查看。 3. 安装 Qt/X11 2.3.2 在 Linux 命令模式下运行以下命令: tar xfz qt-x11-2.3.2.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 的参数中添加别的参数,比如 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 -no-opengl 或-no-xfs,可以键入./configure -help 来获得一些帮助信息。 二、认识 Qt/Embedded 开发环境 Qt/Embedded 的开发环境可以取代那些我们熟知的 UNIX 和 WINDOWS 开发工具。它提供了几个跨平台的 工具使得开发变得迅速和方便,尤其是它的图形设计器。Unix 下的开发者可以在 PC 机或者工作站使用虚 拟缓冲帧,从而可以仿真一个和嵌入式设备的显示终端大小,象素相同的显示环境。 嵌入式设备的应用可以在安装了一个跨平台开发工具链的不同的平台上编译。最通 常的做法是在一个UNIX系统上安装跨平台的带有libc库的GNU c++编译器和二进制工具。 在开发的许多阶段,一个可替代的做法是使用Qt的桌面版本,例如Qt/X11或是 Qt/Windows来进行开发。这样开发人员就可以使用他们熟悉的开发环境,例如微软的 Visual C++ 或者 Borland C++;在UNIX操作系统下,许多环境也是可用的,例如 Kdevelop,它也支持交互式开发。 如果 Qt/Embedded 的应用是在 UNIX 平台下开发的话,那么它就可以在开发的机器 上以一个独立的控制台或者虚拟缓冲帧的方式来运行,对于后者来说,其实是有一个 X11 的应用程序虚拟了一个缓冲帧。通过指定显示设备的宽度,高度和颜色深度,虚拟出来 的缓冲帧将和物理的显示设备在每个像素上保持一致。这样每次调试应用时开发人员就 不用总是刷新嵌入式设备的 FLASH 存储空间,从而加速了应用的编译、链接和运行周期。 运行 Qt 的虚拟缓冲帧工具的方法是:在 Linux 的图形模式下运行命令: qvfb (回车) 当 Qt 嵌入式的应用程序要把显示结果输出到虚拟缓冲帧时,我们在命令行运行这 个程序时,在程序名后加上-qws 的选项。例如: $> hello -qws 2.1 QT 的支撑工具 Qt 包含了许多支持嵌入式系统开发的工具,其中一些工具我们会在别的地方介绍。 有两个最实用的工具(除了上面我们提到的虚拟缓冲帧)是 qmake 和 Qt designer(图 形设计器)。 qmake 是一个为编译 Qt/Embedded 库和应用而提供的 Makefile 生成器。它能够根据 一个工程文件(.pro)产生不同平台下的 Makefile 文件。qmake 支持跨平台开发和影子 生成(shadow builds),影子生成是指当工程的源代码共享给网络上的多台机器时,每 台机器编译链接这个工程的代码将在不同的子路径下完成,这样就不会覆盖别人的编译 链接生成的文件。qmake 还易于在不同的配置之间切换。 开发者可以使用 Qt 图形设计器可视化地设计对话框而不需编写一行代码。使用 Qt 图形设计器的布局管理可以生成具有平滑改变尺寸的对话框,qmake 和 Qt 图形设计器是 完全集成在一起的。 2.2 信号与插槽 信号与插槽机制提供了对象间的通信机制,它易于理解和使用,并完全被 Qt 图形 设计器所支持。 图形用户接口的应用需要对用户的动作做出响应。例如,当用户点击了一个菜单项 或是工具栏的按钮时,应用程序会执行某些代码。大部分情况下,我们希望不同类型的 对象之间能够进行通信。程序员必须把事件和相关代码联系起来,这样才能对事件做出 响应。以前的工具开发包使用的事件响应机制是易崩溃的,不够健壮的,同时也不是面 向对象的。Trolltech 已经创立了一种新的机制,叫做“信号与插槽”。信号与插槽是一 种强有力的对象间通信机制,它完全可以取代原始的回调和消息映射机制;信号与插槽 是迅速的,类型安全的,健壮的,完全面向对象并用 C++来实现的一种机制。 在以前,当我们使用回调函数机制来把某段响应代码和一个按钮的动作相关联时, 我们通常把那段响应代码写成一个函数,然后把这个函数的地址指针传给按钮,当那个 按钮被按下时,这个函数就会被执行。对于这种方式,以前的开发包不能够确保回调函 数被执行时所传递进来的函数参数就是正确的类型,因此容易造成进程崩溃,另外一个 问题是,回调这种方式紧紧的绑定了图形用户接口的功能元素,因而很难把开发进行独 立的分类。 Qt的信号与插槽机制是不同的。Qt的窗口在事件发生后会激发信号。例如一个按钮 被点击时会激发一个“clicked”信号。程序员通过建立一个函数(称作一个插槽), 对象1 信号 1 信号 2 connect( 对象1, 信号1, 对象2, 插槽1 ) connect( 对象1, 信号1, 对象2, 插槽2 ) 对象 2 信号 1 对象 3 信号 1 插槽 1 插槽 2 connect( 对象1, 信号2, 对象4, 插槽1 ) 对象 4 插槽 1 connect( 对象3, 信号1, 插槽 1 插槽 2 插槽 3 对象4, 插槽3 ) 图一 一些信号与插槽连接的抽象图 然后调用 connect()函数把这个插槽和一个信号连接起来,这样就完成了一个事件和响 应代码的连接。信号与插槽机制并不要求类之间互相知道细节,这样就可以相对容易的 开发出代码可高重用的类。信号与插槽机制是类型安全的,它以警告的方式报告类型错 误,而不会使系统产生崩溃。 例如,如果一个退出按钮的clicked() 信号被连接到了一个应用的退出函数- 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 quit() 插槽。那么一个用户点击退出键将使应用程序终止运行。上述的连接过程用代 码写出来就是这样 connect( button, SIGNAL(clicked()), qApp, SLOT(quit()) ); 我们可以在Qt应用程序的执行过程中增加或是减少信号与插槽的连接。 信号与插槽的实现扩展了C++的语法,同时也完全利用了C++面向对象的特征。信号 与插槽可以被重载或者重新实现,它们可以定义为类的公有,私有或是保护成员。 2.2.1 信号与插槽的例子 如果一个类要使用信号与插槽机制,它就必须是从QObject或者QObject的子类继承,而 且在类的定义中必须加上Q_OBJECT宏。信号被定义在类的信号部分,而插槽则定义在public slots, protected slots 或者 private slots 部分。 下面定义一个使用到信号与插槽机制的类。 class BankAccount : public QObject { Q_OBJECT public: BankAccount() { curBalance = 0; } int balance() const { return curBalance; } public slots: void setBalance( int newBalance ); signals: void balanceChanged( int newBalance ); private: int curBalance; }; 和大部分的C++的类一样,BankAccount类有一个构造函数,还有一个取值的函数 balance(),一个设置值的函数setBalance( int newBalance )。 这个类有一个信号balanceChanged(),这个信号声明了它在BankAccount类的成员 curBalance的值被改变时产生。信号不需要被实现,当信号被激发时,和该信号连接的插槽 将被执行。 上面用来设置值的函数 setBalance( int newBalance )定义在类的“public slots” 部分,因此它是一个插槽。插槽是一个需要实现的标准的成员函数,它可以像其它函数一样 被调用,也可以和信号相连接。 下面就是该插槽函数 setBalance( int newBalance )的实现代码: void BankAccount::setBalance( int newBalance ) { if ( newBalance != curBalance ) { curBalance = newBalance; emit balanceChanged( curBalance ); } } 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 其中的一段代码 emit balanceChanged( 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 并未对一个窗体有什么限制,任何类型的窗体可以是顶级窗体,任何类型 的窗体可以是别的窗体的子窗体。在父窗体显示区域的子窗体的位置可以通过布局管理 自动的进行设置,也可以人为的指定。当父窗体无效,隐藏或被删除后,它的子窗体都 会进行同样的动作。 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 标签,消息框,工具栏等等等,并未被限制使用什么颜色,字体和语言。Qt的文本 呈现窗体可以使用HTML子集显示一个多语言的宽文本。 2.3.1 一个 Hello 的例子 下面是一个显示“Hello Qt/Embedded!”的程序的完整的源代码: 图三 Hello 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(); } 2.3.2 通用窗体 下面是一些主要的 Qt 窗体的截屏图,这些窗体使用了窗口样式。 图四 使用了 QHBox 进行排列一个标签和一个按钮 图五 使用了QbuttonGroup的两个单选框和两个复选框 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 图六 使用了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。 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 2.3.3 画布 QCanvas 类提供了一个高级的平面图形编程接口,它可以处理大量的像线条、矩形、椭 圆、文本、位图、动画等这些画布项,画布项可以较容易的做成交互式的(例如做成支持用 户移动的)。 画布项是QcanvasItem子类的实例,它们比窗体类Qwidget更显得轻量级,它们能够被快 速的移动,隐藏和显示。Qcanvas可以更有效的支持冲突检测,它能够列出一个指定区域里 面的所有的画布项。QcanvasItem可以被子类化,从而可以提供更多的客户画布项类型,或 者扩展已有的画布项的功能。 Qcanvas对象是由QcanvasView进行绘制的,QcanvasView对象可以以不同的译文、比例、 旋转角度,剪切方式去显示同一个画布。 Qcanvas 对象是理想的数据表现方式,它已经被消费者用于绘制地图和显示网络拓扑结 构。它也可用于制作快节奏的且有大量角色的平面游戏。 图九 在 Qtopia 中用 QCanvas 实现的小行星游戏 2.3.4 客户窗体 通过对Qwidget或者它的子类进行子类化,我们可以建立自己的客户窗体或者对话框。下面 是一个完整的源代码例子,它示例了如何通过子类化窗体,绘制一个模拟的时钟。 AnalogClock 窗体类是Qwidget的子类,它显示当前时间,并且可以自动地更新时间。 图十 模拟钟窗体 在 analogclock.h头文件中, AnalogClock 以这样地形式定义:: #include class AnalogClock : public QWidget { public: AnalogClock( QWidget *parent = 0, const char *name = 0 ); protected: virtual void timerEvent( QTimerEvent *event ); virtual void paintEvent( QPaintEvent *event ); }; AnalogClock 类继承了Qwidget, 它有一个典型的窗体类构造函数,这个函数有父窗口对象 指针和名字指针两个参数。(如果设置了名字的话,测试和调试起来就会容易些) timerEvent() 函数是从QObject (Qwidget的父类)对象继承而来的,这个函数会被系统定 期调用。paintEvent() 函数是从QWidget 继承而来的并且当窗体需要重画时这个函数就会 被调用。 timerEvent() 和 paintEvent() 函数是“事件句柄”的两个例子。应用对象以 重载父类对象的虚拟函数events (QEvent objects) 的形式接收系统的事件。大约有超过50 个的系统事件是较常用的,例如 MouseButtonPress, MouseButtonRelease, KeyPress, KeyRelease, Paint, Resize 和Close. 对象可以对发给它们的事件做出响应或 者筛选一些事件后再发送给别的对象。 analogclock.cpp 文件是定义在analogclock.h中的函数的实现源文件 #include #include #include "analogclock.h" AnalogClock::AnalogClock( QWidget *parent, const char *name ) : QWidget( parent, name ) { startTimer( 12000 ); resize( 100, 100 ); } void AnalogClock::timerEvent( QTimerEvent * ) { update(); } void AnalogClock::paintEvent( QPaintEvent * ) { QCOORD hourHand[8] = { 2, 0, 0, 2, -2, 0, 0, -25 }; QCOORD minuteHand[8] = { 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++ ) 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 { 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(); } 构造函数设置窗口的尺寸大小为100 x 100,并且告诉系统每隔12秒调用一次 timerEvent()函数,从而对模拟钟的窗体进行刷新。 在 timerEvent()函数中, 通过调用QWidget的函数 update()就可以告诉Qt,窗体需要 立即重画,紧接着Qt就会产生一个绘制事件并且调用paintEvent()函数。 在paintEvent()函数中,一个Qpainter对象用于在窗体上绘制12个刻度以及分针,时针。 Qpainter类提供了一种统一的方式用于绘制窗体,位图,矢量图等,它提供了绘制点,线, 椭圆,多边形,弧,贝塞尔曲线等功能,一个Qpainter的坐标系可以被转变,缩放,旋转, 和剪切,这样对象就可以根据它在窗口或者窗体上的位置绘制出一个剪切的视图。剪切可以 使窗体绘制时减少闪烁。使用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 ); clock->show(); return app.exec(); } 2.3.5 主窗口 QMainWindow类是为应用的主窗口提供一个摆放相关窗体的框架。 一个主窗口包含了一组标准窗体的集合。主窗口的顶部包含一个菜单栏,它的下方放置 着一个工具栏,工具栏可以移动到其它的停靠区域。主窗口允许停靠的位置有顶部,左边, 右边和底部。工具栏可以被拖放到一个停靠的位置,从而形成一个浮动的工具面板。主窗口 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 的下方,也就是在底部的停靠位置之下有一个状态栏。主窗口的中间区域可以包含其它的窗 体。提示工具和“这是什么”帮助按钮以旁述的方式阐述了用户接口的使用方法。 对于小屏幕的设备,使用Qt图形设计器定义的标准的Qwidget模板比使用主窗口类更好 一些。典型的模板包含有菜单栏,工具栏,可能没有状态栏(在必要的情况下,可以用任务 栏,标题栏来显示状态) 2.3.6 菜单 弹出式菜单QpopupMenu类以垂直列表的方式显示菜单项,它可以是单个的(例如上下文 相关菜单),可以以菜单栏的方式出现,或者是别的弹出式菜单的子菜单出现。 每个菜单项可以有一个图标,一个复选框和一个加速器(快捷键),菜单项通常对应一 个动作(例如存盘),分隔器通常显示成一条竖线,它用于把一组相关联的动作菜单分立成 组。 下面是一个建立包含有 New,Open 和 Exit 菜单项的文件菜单的例子。 QPopupMenu *fileMenu = new QPopupMenu( this ); fileMenu->insertItem( "&New", this, SLOT(newFile()), CTRL+Key_N ); fileMenu->insertItem( "&Open...", this, SLOT(open()), CTRL+Key_O ); fileMenu->insertSeparator(); fileMenu->insertItem( "E&xit", qApp, SLOT(quit()), CTRL+Key_Q ); 当一个菜单项被选中,和它相关的插槽将被执行。加速器(快捷键)很少在一个没有键盘输 入的设备上使用,Qt/Embedded 的典型配置并未包含对加速器的支持。上面出现的代码 “&New”意思是在桌面机器上以“New”的方式显示出来,但是在嵌入式设备上,它只会显 示为“New”。 QmenuBar类实现了一个菜单栏,它会自动的设置几何尺寸并在它的父窗体的顶部显示出 来,如果父窗体的宽度不够宽以致不能显示一个完整的菜单栏,那么菜单栏将会分为多行显 示出来。Qt内置的布局管理能够自动的调整菜单栏。 Qt的菜单系统是非常灵活的,菜单项可以被动态的使能,失效,添加或者删除。通过子 类化QcustomMenuItem,我们可以建立客户化外观和功能的菜单项。 2.3.7 工具栏 QtoolButton类实现了一个带有图标,3维边框和可选标签的工具栏按钮。切换工具 栏按钮具有开、关的特征,其它的按钮则执行一个命令。不同的图标用来表示按钮的活 动,无效、使能模式,或者是开或关的状态。如果你仅为按钮指定了一个图标,那么Qt 会使用可视提示来表现按钮不同的状态,例如按钮失效时显示灰色。 工具栏按钮通常以一排的形式显示在工具栏上,对于一个有几组工具栏的应用, 用户可以随便的到处移动这些工具栏,工具栏差不多可以包含所有的窗体,例如 QComboBoxes 和 QspinBoxes。 2.3.8 旁述 现代的应用主要使用旁述的方式去解释用户接口的用法。Qt 提供了两种旁述的方式: 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 “提示栏”和“这是什么”帮助按钮。 “提示栏”是小的,通常是黄色的矩形,当鼠标在窗体的一些位置游动时它就会自动出 现。它主要用于解释工具栏按钮,特别是那些缺少文字标签说明的工具栏按钮的用途。下面 就是如何设置一个“存盘”按钮的提示的代码。 QToolTip::add( saveButton, "Save" ); 当提示字符出现之后,你还可以在状态栏显示更详细的文字说明。 对于一些没有鼠标的设备(例如那些使用触点输入的设备),就不会有鼠标的光标在窗 体上进行游动,这样就不能激活提示栏。对于这些设备也许就需要使用“这是什么”帮助按 钮,或者使用一种姿态来表示输入设备正在进行游动,例如用按下或者握住的姿态来表示现 在正在进行游动。 “这是什么”帮助按钮和提示栏有些相似,只不过前者是要用户点击它才会显示旁述。 在小屏幕设备上,要想点击“这是什么”帮助按钮,具体的方法是,在靠近应用的 X 窗口的 关闭按钮“x”附近你会看到一个“?”符号的小按钮,这个按钮就是“这是什么”帮助按 钮。一般来说,“这是什么”帮助按钮按下后要显示的提示信息应该比提示栏要多一些。下 面是设置一个存盘按钮的“这是什么”文本提示信息的方法: QWhatsThis::add( saveButton, "Saves the current file." ); QToolTip 和 QWhatsThis 类提供了虚拟函数以供开发者重新实现更多的特定的用途。 Qtopia并未使用上述提及的两种帮助(旁述)机制。它在应用窗口的标题栏上放置 一个“?”符号的按钮来代替上述的旁述机制,这个“?”按钮可以启动一个浏览器来 显示和当前应用相关的HTML页面。Qtopia使用按下和握住的姿态来调用上下文菜单(右 击)和属性对话框。 2.3.9 动作 应用程序通常提供给用户几种不同的方式去执行特别的动作。例如,大部分应用提供了 一个“Save”动作给用于存盘的菜单(File|Save)以及工具栏(一个“软盘”图标的工具栏 按钮)和快捷键(Ctrl+S)。Qaction类可以让上述过程变得简洁,它允许程序员在一个地方 定义一个动作,然后把这个动作加入到菜单或者工具栏,这个过程与把菜单项加入到菜单的 道理是一样的。 下面的代码实现了一个“Save”菜单项和一个“Save”工具按钮,旁述系统和快捷键可 以很容易的添加进去,但是我们没添加,因为它们很少在嵌入式设备上使用。 QAction *saveAct = new QAction( this ); saveAct->setText( "Save" ); saveAct->setIconSet( QPixmap("save.png") ); connect( saveAct, SIGNAL(activated()), this, SLOT(save()) ); saveAct->addTo( fileMenu ); saveAct->addTo( toolbar ); 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 除了不用复制代码,使用 Qaction 还可以确保菜单的状态与工具栏按钮的状态保持一 致,必要的时候还可显示提示栏。使一个动作(Action)失效将导致和该动作相关联的菜单 项以及工具按钮的失效。同样的,如果用户切换一个工具按钮的状态,那么相关的菜单项的 也会跟着被选中或不选中。 2.4 对话框 使用 Qt 图形设计器这个可视化设计工具用户可以建立自己的对话框。Qt 使用布局 管理自动的设置窗体与别的窗体之间相对的尺寸和位置,这样可以确保对话框能够最好 的利用屏幕上的可用空间。使用布局管理意味着按钮和标签可以根据要显示的文字自动 的改变自身大小,而用户完全不用考虑文字是那一种语言。 2. 4. 1 布局 Qt 的布局管理用于组织管理一个父窗体区域内的子窗体。它的特点是可以自动的设 置子窗体的位置和大小,并可判断出一个顶级窗体的最小和缺省的尺寸,当窗体的字体 或内容变化后,它可以重置一个窗体的布局。 使用布局管理,开发者可以编写独立于屏幕大小和方向之外的程序,从而不需要浪 费代码空间和重复编写代码。对于一些国际化的应用程序,使用布局管理,可以确保按 钮和标签在不同的语言环境下有足够的空间显示文本,不会造成部分文字被剪掉。 布局管理使得提供部分用户接口组件,例如输入法和任务栏变得更容易。我们可以 通过一个例子说明这一点,当Qtopia的用户正在输入文字时,输入法会占用一定的文字 空间,应用程序这时也会根据可用的屏幕尺寸的变化调整自己。 图十 Qtopia 的布局管理 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 Qt提供了三种用于布局管理的类:QHBoxLayout, QVBoxLayout 和 QgridLayout。 图十一 QHBoxLayout, QVBoxLayout 和 QGridLayout 的布局效果 QHBoxLayout 布局管理把窗体按照水平方向从左至右排成一行 QVBoxLayout 布局管理把窗体按照垂直方向从上至下排成一列 QGridLayout 布局管理以网格的方式来排列窗体,一个窗体可以占据多个网格。 在多数情况下,布局管理在管理窗体时执行最优化的尺寸,这样窗口看起来就更好看而 且可以尺寸变化会更平滑。使用以下的机制可以简化窗口布局的过程: 1、 为一些子窗口设置一个最小的尺寸,一个最大的或者固定的尺寸。 2、 增加拉伸项(stretch items)或者间隔项 (spacer item)。拉伸项和间隔项可以填充 一个排列的空间。 3、 改变子窗口的尺寸策略,程序员可以调整窗体尺寸改变时的一些策略。子窗体可以被设 置为扩展,紧缩和保持相同尺寸等策略。 4、 改变子窗口的尺寸提示。QWidget::sizeHint() 和 QWidget::minimumSize-Hint() 函 数返回一个窗体根据自身内容计算出的首选尺寸和首选最小尺寸,我们在建立窗体时可 考虑重新实现这两个函数。 5、设置拉伸比例系数。设置拉伸比例系数是指允许开发者设置窗体之间占据空间大小的比 例系数,例如我们设定可用空间的 2/3 分配给窗体 A,剩下的 1/3 则分配给窗体 B。 布局管理也可按照从右至左,从下到上的方式来进行。当一些国际化的应用需要支 持从右至左阅读习惯的语言文字(例如阿拉伯和希伯来)时,使用从右至左的布局排列 是更方便的。 布局是可以嵌套的和随意进行的。下面是一个对话框的例子,它以两种不同尺寸大 小来显示: 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 图十二 小的对话框和大的对话框 这个对话框使用了三种排列方式。QVBoxLayout管理一组按钮,QHBoxLayout管理一个显 示国家名称的列表框和右边那组按钮,QVBoxLayout管理窗体上剩下的组件“Now please select a country”标签。在“< Prev”和“Help”按钮之间放置了一个拉伸项(stretch items), 使得两者之间保持了一定比例的间隔。 建立这个对话框窗体和布局管理的实现代码如下: QVBoxLayout *buttonBox = new QVBoxLayout( 6 ); buttonBox->addWidget( new QPushButton("Next >", this) ); buttonBox->addWidget( new QPushButton("< Prev", this) ); buttonBox->addStretch( 1 ); buttonBox->addWidget( new QPushButton("Help", this) ); QListBox *countryList = new QListBox( this ); countryList->insertItem( "Canada" ); /* … */ countryList->insertItem( "United States of America" ); QHBoxLayout *middleBox = new QHBoxLayout( 11 ); middleBox->addWidget( countryList ); middleBox->addLayout( buttonBox ); QVBoxLayout *topLevelBox = new QVBoxLayout( this, 6, 11 ); topLevelBox->addWidget( new QLabel("Now please select a country", this) ); topLevelBox->addLayout( middleBox ); 使用 Qt 图形设计器设计的这个对话框,显示如下 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 图十三 Qt 图形设计器中使用了布局的对话框 2. 4. 2 Qt 图形设计器 Qt 图形设计器是一个具有可视化用户接口的设计工具。Qt 的应用程序可以完全用 源代码来编写,或者使用 Qt 图形设计器来加速开发工作。启动 Qt 图形设计器的方法是: 在 Linux 命令模式下,键入以下命令(假设 Qt X11 安装在/usr/local 下): cd qt-2.3.2/bin ./designer 这样就可以启动一个与 Windows 下的 Delphi 相类似界面。 下图是使用 Qt 图形设计器 设计一个表单的截屏图。 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 图十四 Qt 图形设计器 开发者点击工具栏上的代表不同功能的子窗体/组件的按钮,然后把它放到一个表 单上面,这样就可以把一个子窗体/组件放到表单上了。开发者可以使用属性对话框来 设置子窗体的属性。精确的设置子窗体的位置和尺寸大小是没必要的。开发者可以选择 一组窗体,然后对他们进行排列。例如,我们选定了一些按钮窗体,然后使用“水平排 列(lay out horizontally)”选项对它们进行一个接一个的水平排列。这样做使得设 计工作变得更快,而且完成后的窗体将能够按照属性设置的比例填充窗口的可用尺寸范 围。 使用 Qt 图形设计器进行图形用户接口的设计可以消除应用的编译,链接和运行时 间,同时使得修改图形用户接口的设计变得更容易。Qt 图形设计器的预览功能可以使开 发者能够在开发阶段看到各种样式的图形用户界面,包括客户样式的用户界面。通过 Qt 集成的功能强大的数据库类,Qt 图形设计器还可提供生动的数据库数据浏览和编辑操 作。 开发者可以建立同时包含有对话框和主窗口的应用,其中主窗口可以放置菜单,工 具栏,旁述帮助等等的子窗口部件。Qt 图形设计器提供了几种表单模板,如果窗体会被 多个不同的应用反复使用,那么开发者也可建立自己的表单模板,以确保窗体的一致性。 Qt 图形设计器使用向导来帮助人们更快更方便的建立包含有工具栏、菜单和数据库 等方面的应用。程序员可以建立自己的客户窗体,并把它集成到 Qt 图形设计器中。 Qt 图形设计器设计的图形界面以扩展名为“ui”的文件进行保存,这个文件有良好 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 的可读性,这个文件可被 uic(Qt 提供的用户接口编译工具)编译成为 C++的头文件 和源文件。Qmake 工具在它为工程生成的 Makefile 文件中自动的包含了 uic 生成头文件 和源文件的规则。 另一种可选的做法是,在应用程序运行期间载入 ui 文件,然后把它转变为具备原 先全部功能的表单。这样开发者就可以在程序运行期间动态修改应用的界面,而不需重 新编译应用,另一方面,也使得应用的文件尺寸减小了。 2. 4. 3 建立对话框 Qt 为许多通用的任务提供了现成的包含了实用的静态函数的对话框类,下边是一些 Qt 的标准的对话框的截屏图。 QmessageBox类是一个用于向用户提供信息或是给用户进行一些简单选择(例如 “yes”或“no”)的对话框类。 图十五 一个 QMessageBox 对话框 progressDialog对话框包含了一个进度栏和一个“Cancel”按钮 图十六 一个 QprogressDialog 对话框 Qwizard类提供了一个向导对话框的框架 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 图十七 一个向导类 Qt提供的对话框还包括QColorDialog, QFileDialog, QFontDialog 和 QprintDialog。这些类通常适用于桌面应用,一般不会在Qt/Embedded中 编译使用它们。 2.5 外形与感觉 Qt桌面应用随着执行环境(例如Windows XP, Mac OS X, Linux)的不同而具有不 同的样式,或者叫做外形与感觉。Qt/Embedded 的应用可以使用不同操作环境提供的样 式,也可以以静态或插件的方式使用客户样式。开发者可以设计自己窗体的样式和窗口 装饰。 2.5.1 窗体样式 一个样式是一个实现Qt窗体外形与视觉效果的类Qstyle的子类。Qt/Embedded 程序 员可以自由的使用和修改目前存在的样式,也可以使用Qt样式设计引擎实现自己的样 式。目前Qt/Embedded提供的样式类型有EmbeddedareWindows, Motif, MotifPlus,CDE, Platinum 和 SGI。样式可以被动态的设置到一个应用中,甚至可以设置到特定的窗体 上。 图十八 以不同样式建立的下拉框 通过给一组相关的应用编写一个客户样式,可以让这些应用的外观具有与众不同的 感觉。建立客户样式可以通过子类化QStyle,QcommonStyle或者QcommonStyle的派生类 来实现。通过重新实现现有样式基类的一两个虚函数可以很容易对现有样式做一些小的 修改。 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 一个样式可以编译成为一个插件,在 Qt 图形设计器中,把样式做成插件的话,开 发者就可以以设备的客户样式来预览设计出来的表单。样式插件使得开发者不需重新编 译就可改变设备的外观。 图十九 Qstyle 的继承图 Qt发布的窗体都是样式已知的,当它们的样式改变后,窗体会自动重绘。客户窗体 和对话框大多数都是Qt原先发布的窗体和布局的重新整合。它们也是自动的获得原先的 样式。在少数场合,当有必要写一个外观上很随意的窗体时,开发者可以使用QStyle 去写一些用户接口元素,而不是直接的绘制固有的矩形。 2.5.2 窗口装饰 顶级窗口的装饰是由一个标题栏和一个框架组成的。Qt/Embedded 包括了这些窗口 样式:BeOS, Hydro, KDE 和 Windows。 图二十 不同的平台上的窗口风格 如果需要的话,我们可以配置不同的窗口使用不同的窗口装饰。通过子类化 QWSDecoration,我们可以建立客户的装饰样式,并且把它们以插件的形式进行发布。为 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 了在窗口管理器之外进行更多的控制行为,开发者需要子类化QWSManager。 2.6 国际化 Qt/Embedded完全支持Unicode,一个国际标准的字符集。开发者在他们的应用中可 以自由的混合使用被Unicode字符集支持的语言,例如阿拉伯文,英文,中文,希伯来 文,日文和俄文等。为了有助于公司将产品推向国际市场,Qt还提供了将应用翻译成支 持多种语言环境的工具。 2.6.1 Unicode Qt 使用 QString 存储 Unicode 编码的字符串,Qstring 取代了粗糙的 const char *; 它提供了用于处理 Qstring 和 const char *之间相互转换的构造函数和操作符。因为 Qt 使 用了隐式共享(写时复制)技术来减少内存的使用,所以直接复制 Qstring 的值是不会产生问 题的。为有效率的存储 ASII 码字符串,Qt 还提供了 QCString 类。 Qt 为所有要显示在屏幕上的文本,包括最简单的文字标签到最复杂的宽文本编辑器, 提供了一个强大的 Unicode 文本呈现引擎。这个引擎支持一些先进的特征,例如特殊的间隔 线、双向写和区别标记。它几乎支持世界上所有的书写系统,包括阿拉伯文,中文,古斯拉 夫文,英文,希腊文,希伯来文,日文,韩文,拉丁和越南文。体现这个引擎的最优化性能 的常用的例子就是:在带有加速功能的文字的下方显示一条下划线(例如 File)。 QtextCodec 的子类用于处理不同编码类型的字符集之间的转换。Qt 3.0 支持 37 种不同 的编码方式,包括中文的 Big5 和 GBK,日文的 EUC-JP、JIS 和 Shift-JIS,俄罗斯的 KOI8-R 和 ISO 8859 系列。它们可以以库的一部分或者插件的形式编译,或者使用“feature”机制 去除这些编译。 2.6.2 应用的翻译 Qt 提供了相应的工具和函数用于帮助开发者以他们的本地语言推出应用。 要使一个字符串可以被翻译,你需要把这个字符串作为一个参数放到 tr()函数中调 用。例如: saveButton->setText( tr("Save") ); Qt 尝试寻找字符串"Save"的译文,如果找到的话,就会把它的译文显示出来,如果找 不到,就用原来的字符串"Save"进行显示。例如我们把英文作为源语言,现在我们要把这个 源语言翻译成中文,即中文为翻译的目标语言,以此类推。这样当我们调用 tr()函数后, 函数的参数的原先的缺省编码就会转变为 Unicode 编码。Tr()函数的通常用法为: Context::tr("source text", "comment") 上面的 “Context” 是指一个 QObject 对象的子类的名称。如果在一个包含了 tr()成员 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 函数的类的上下文环境中使用 tr()函数时,“Context”通常可以省略掉。例如: Context::func1() { setText( tr("Save") ); } “source text”是指要翻译的文本的内容 “comment”是一个可选项,它用于给手工翻译者提供一些额外的信息。 说了半天,还有一个重要的内容我们没谈,就是 tr()函数如何寻找到译文。我们要 把一个源字符串翻译为和它对应的译文(目标语言的字符串)时,我们需要让 Qt 知道这些 译文放在哪里。Qt 规定了译文存储在 QTranslator 对象中,这个对象是从一个内存映射文 件(扩展名为 pm)中读取译文。每个 pm 文件包含了某种语言的译文信息。所以开发者需要 建立一个 pm 文件来存储应用中需要翻译的字符串的译文。 Qt 提供了 3 种工具帮助人们建立译文存储文件(.pm 文件),这 3 个工具是 lupdate, Qt Linguist 和 lrelease. 1、lupdate 自动地从源代码文件(.cpp)和界面接口文件(.ui)中获取所有需要翻译的对 象,即上述的(“Context”);同时还获取要翻译的所有的字符串(源语言的字符串), 即上述的“source text”;“comment”选项如果被使用的话,也会被纳入收集的范围。当 这些信息收集完毕后,lupdate 最后会生成一个.ts 文件(翻译源文件),这个文件是直接 可阅读的。 2、开发者使用 Qt Linguist 工具提供的良好的人机界面打开一个.ts 文件(翻译源文件), 然后开发者根据每一个 source text 填写上相对应的译文,这样一个.ts 文件(翻译源文件) 就包含了完整的“Context”,“source text”和译文信息。 3、最后通过运行 lrelease 去把一个.ts 文件(翻译源文件)压缩为一个.pm 文件(译文存 储文件),生成的.pm 文件可用在嵌入式设备上。 在一个应用的生存期间里,上述的步骤有可能根据需要会被反复执行。多次运行 lupdate 是很安全的,你可以重复使用已经存在的译文(.pm)文件,或者当你不想使用某 些翻译文件时你可以标记这些翻译源文件为旧文件,而不需要删除它们。 本页已使用福昕阅读器进行编辑。 福昕软件(C)2005-2010,版权所有, 仅供试用。 Qt 嵌入式图形开发(实战篇) 作者:深圳市优龙科技有限公司 时间:2004/7/6 前面我们详细介绍了 Qt 嵌入式工具开发包的安装和使用方法,但是这个介绍对于要真 正进行一次商业的嵌入式应用开发来说并不足够。嵌入式应用的开发工作基本上是在工作站 或是 PC 机上完成的,我们在工作的机器上调试运行嵌入式应用,并将输出结果显示在一个仿 真小型设备显示终端的模拟器上。在开发的后期,我们要根据我们选择的嵌入式硬件平台, 将嵌入式应用编译链接成适合在这个硬件平台上运行的二进制目标代码,另外由于应用使用 到了 Qt/Embedded 的库,所以我们还要将 Qt/Embedded 库的源代码编译链接成为适合在这个 硬件平台上使用的二进制目标代码库。当一个 Qt/Embedded 应用被部署到小型设备上,并可 靠的运行,这样一个开发过程才宣告结束。 使用 Qt/Embedded 开发一个嵌入式应用的过程大体可用下面的流程图表示: 选定嵌入式硬件平台 在工作的机器上安装 Qt/Embedded 工具开发包 根据目标硬件平台,交叉编译 Qt/Embedded 的库 在工作的机器上进行应用程序的编码,调试 根据目标硬件平台,交叉编译嵌入式应用 在嵌入式硬件设备上调试运行应用 调试有无问题 无 发布嵌入式应用 有问题 图一 Qt/Embedded 应用开发的一般流程 在以下的篇幅,我们将按照上述的流程完整的介绍使用 Qt/Embedded 开发一个嵌入式应 用例子的过程。 一、 嵌入式硬件开发平台的选择 嵌入式系统的核心部件是各种类型的嵌入式处理器,目前据不完全统计,全世界嵌入式 处理器的品种总量已经超过 1000 多种,流行体系结构有 30 几个系列,如何在种类纷繁的嵌 入式处理器中选择适合应用需求的处理器呢?在应用的需求分析过程中,有几项因素决定了 应该选择什么样的嵌入式处理器:①嵌入式处理器能否在技术上实现应用②嵌入式处理器的 成本是否符合应用要求。对于嵌入式处理器能否在技术上实现应用需要考虑到应用在运行时 的特点,比如应用对实时性的要求,对计算量和计算速度的要求,对外围接口电路的要求, 对图形用户接口的要求等。当我们选定了一个嵌入式处理器之后,我们还要考虑选用什么样 的操作系统,在选择操作系统时我们也要注意嵌入式处理器能否被所选的操作系统支持,有 没有合适的编译器能够生成支持这种操作系统和嵌入式处理器的二进制目标代码。 近年来,随着 EDI 的推广和 VLSI 设计的普及化,及半导体工艺的迅速发展,在一个硅 片上实现一个更为复杂的系统的时代已来临,这就是 System On Chip(SOC)。各种通用处理 器内核将作为 SOC 设计公司的标准库,和许多其它嵌入式系统外设一样,成为 VLSI 设计中 一种标准的器件,用标准的 VHDL 等语言描述,存储在器件库中。用户只需定义出其整个应 用系统,仿真通过后就可以将设计图交给半导体工厂制作样品。这样除个别无法集成的器件 以外,整个嵌入式系统大部分均可集成到一块或几块芯片中去,应用系统电路板将变得很简 洁,对于减小体积和功耗、提高可靠性非常有利。 SOC 可以分为通用和专用两类。通用系列包括 Infineon 的 TriCore,Motorola 的 M-Core, 某些 ARM 系列器件,Echelon 和 Motorola 联合研制的 Neuron 芯片等。专用 SOC 一般专用于 某个或某类系统中,不为一般用户所知。一个有代表性的产品是 Philips 的 Smart XA,它 将 XA 单片机内核和支持超过 2048 位复杂 RSA 算法的 CCU 单元制作在一块硅片上,形成一 个可加载 JAVA 或 C 语言的专用的 SOC,可用于公众互联网如 Internet 安全方面。 由于 SOC 芯片集成了处理器和许多常用的外围接口芯片,使得它的功能变得很强大,能 够应用的领域和场合变得很广,所以嵌入式开发板的厂商选择 SOC 芯片作为开发板的核心芯 片。三星的 S3C2410 就是这样一款带有 ARM920T 处理器内核的 SOC 芯片,由于它主频高, 内置 LCD 和触摸屏控制器,以及声音控制器等外围电路,因而用在对图形用户接口有较高要 求的场合是非常合适的。因为支持 ARM9 处理器的 linux 编译器早已发布,所以 S3C2410 可 以很好的支持 linux 和 Qt/Embedded 的运行。 深圳优龙科技有限公司设计开发的 FS2410 嵌入式开发板是一个成熟稳定的嵌入式开 发硬件平台,它使用了三星的 S3C2410 作为核心芯片。以下是 FS2410 的一些资料(更详细 的资料可参考优龙公司的《FS2410 开发板使用手册》)。在本文提及的 Qt/Embedded 应用 例子都是居于 FS2410 嵌入式硬件开发平台的。 1.1 优龙 FS2410 嵌入式开发板介绍 1.1.1 FS2410 开发板资源 技术参数: S3C2410:16-/32-bit ARM920T 内核 系统时钟:使用外部12MHz 晶体由CPU 内部PLL 备频至200MHz BOOT ROM:SST39VF160 NAND FLASH:K9F5608/ K9F1208 SDRAM:64Mbyte(32Mbyte×2) TFT/STN LCD 和触摸屏控制器 2 通道UART 2 个USB 主机控制器(其中一个可配置为USB 设备控制器) SD 卡/MMC 卡主机控制器 Embedded-ICE 调试接口 RTC 实时时钟(具备后备锂电池) IIC 总线接口(驱动AT24C04-SC27) ADC 模数转换接口 SPI 接口 IIS 数字音频输入/输出接口, 音频输出采用UDA1341 Dac EINT 外部中断接口 IrDA 红外线收发器 16 板上轻触键 CS8900, 10M 以太网接口 发光二极管指示灯 1.1.2 FS2410 开发套件构成 FS2410 套件包括: 1) 一块已测试好的FS2410 开发板(包括FS2410 核心板和设备板) 2)LCD 板一块,包含三星3.5 寸256K 色TFT 真彩屏加驱动电路 3)一个SuperJtag 调试头,该调试头可用来烧写2410 的boot 程序 4)一条两母直连串口线 5)一条交叉线网线 6) 一条USB 线 7) 一条并口线 8) 一个+7.5~12V 直流电源 9) 两张FS2410 光盘 10) 一个包装盒 光盘内所附软件: 1.DEMO 程序-开发板测试程序源代码 1) NANDBOOT NAND FLASH 启动引导程序, 通过bios 或jtag 烧入nand flash 0 地址,d9~d12 闪烁 2) u2410mon nor flash 启动,通过usb 下载,通过bios 或jtag 烧入nor flash 0 地址 3) S3C2410_TEST 三星测试程序,包括各种测试程序(IIC,sd),仿真器usb 下运行载, 4) 2410app 自编的测试程序,SD,按键, 2 flash 烧写程序 3 FS2410 LINUX for S3C2410 源代码及开发工具包,含CS8900 网口驱动,串口驱动,USB 驱 动 4 FS2410 三星官方测试代码。 开发系统文档: 1) FS2410 使用手册 2) FS2410 原理图 3) 开发板元器件手册 二、安装 Qt/Embedded 工具开发包 我们要进行 Qt/Embedded 开发,就需要在我们工作的机器上安装 Qt/Embedded 工具开发 包,这一步已在《Qt 嵌入式开发(入门篇)》的第一节中谈及,在此不在敖述。 三、交叉编译 Qt/Embedded 的库 开发居于 Qt/Embedded 的应用程序要使用到 Qt/Embedded 的库,我们编写的 Qt 嵌入式 应用程序最终是在 FS2410 开发板上运行的,因此在把 Qt 嵌入式应用程序编译成支持 FS2410 的目标代码之前,我们需要两样东西,一个是 arm9 的 linux 编译器,另一个是经 arm9 的 linux 编译器编译过的 Qt/Embedded 的库。 ①安装交叉编译工具 我们需要arm9的linux编译器去编译工程并产生arm9处理器的目标代码,而我们却是在 一台PC机或者工作站上(这个机器有可能是x86的处理器)使用这个编译器,这个过程被称 为交叉编译。交叉编译的实际定义是在一个处理器平台上编译产生一个工程代码的另一个处 理器的目标代码。关于如何安装一个arm9的linux编译器请参考《FS2410开发板使用手册》 第七章“如何编译linux”,但是需要特别提醒读者注意,使用toolchain做为交叉编译工具 时,最好使用cross-3.3.2及其以后的版本,这样才能对qt/embeded有良好支持。 ②交叉编译Qt/Embedded库 当我们有了arm9的linux编译器之后,我们就可以使用这个编译器交叉编译Qt/Embedded 库的源代码,从而产生一个以ARM9为目标代码的Qt/Embedded库。具体过程如下 1、 解包 Qt/Embedded(以 Qt/Embedded2.3.7 为例) 解包这个 Qt/Embedded2.3.7 压缩包时,应把它解压缩到不同于你的机器的处理器使用 的 Qt/Embedded2.3.7 的安装路径。 在 Linux 命令模式下运行以下命令: tar xfz qt-embedded-2.3.7.tar.gz 2、配置 Qt/Embedded2.3.7 的安装 Qt/Embedded 的安装选项有很多个,你可以在命令行下直接输入“./configure”来运 行配置,这时安装程序会一步一步提示你输入安装选项。你也可以在“./configure”后输 入多个安装选项直接完成安装的配置。在这些选项中有一个选项决定了编译 Qt/Embedded 库的范围,即可以指定以最小,小,中,大,完全 5 种方式编译 Qt/Embedded 库。另外 Qt/Embedded 的安装选项还允许我们自己定制一个配置文件,来有选择的编译 Qt/Embedded 库,这个安装选项是“-qconfig local”;当我们指定这个选项时,Qt/Embedded 库在安装 过程中会寻找 qt-2.3.7/src/tools/qconfig-local.h 这个文件,如找到这个文件,就会以 该文件里面定义的宏,来编译链接 Qt/Embedded 库。 因为我们使用了优龙公司的 FS2410 嵌入式开发板,所以为了支持触摸屏的显示,我们 需要定义一些宏,为了避免初学者在一开始就直接接触到 Qt/Embedde 的复杂的宏编译选项, 我 们 把 这些 宏 定 义 到 一个 名 为 qconfig-local.h 的 安 装 配置 文 件 中 , 当 读 者在 安 装 Qt/Embedded 的时候,需要把这个文件复制到 Qt/Embedded 的安装路径的/src/tools 子路径 下。例如您安装的是 Qt/Embedded2.3.7,那么您应该把我们提供给您的 qconfig-local.h 安装配置文件复制到 qt-2.3.7/src/tools 路径下。 具体过程如下: cd qt-2.3.7 export QTDIR=$PWD export QTEDIR=$QTDIR cp /配置文件所在路径/qconfig-local.h ./src/tools make clean ./configure –xplatform linux-arm-g++ -shared –debug (接下行) -qconfig local -qvfb -depths 4,8,16,32 make cd .. 四、一个 Hello,World 的例子 下面我们将通过编写一个跳动的“Hello,World”字符串,来讲解 Qt 嵌入式应用的开发 过程。 ① 生成一个工程文件(.pro 文件) 一个应用通常对应一个工程文件,生成一个工程文件,并对它做一些简单的编辑,然后 使用一个专门的工具(例如 tmake)处理这个工程文件,就可以生成一个 Makefile 文件。 产生一个工程文件的其中一个方法是使用 progen 命令(progen 程序可在 tmake 的安装 路径下找到)。下面我们使用 progen 产生一个名为 hello 的工程文件 progen –t app.t –o hello.pro 产生的 hello.pro 工程文件并不完整,开发者还需手动往里添加工程所包含的头文件,源文 件等信息。 ② 新建一个窗体 在 qt-2.3.x for x11 的安装路径的 bin 目录下运行“./designer”命令,就启动了一 个 Qt 图形编辑器。点击编辑器的“new”菜单,弹出了一个“new Form”对话框,在这个对 话框里我们选择“Widget”,然后点击“OK”按钮,这样我们就新建了一个窗体。接着我们 对这个窗体的属性进行设置,注意把窗体的“name”属性设为“Hello”;窗体的各种尺寸设 为宽“240”,高“320”,目的是使窗体大小和 FS2410 带的显示屏大小一致;窗体背景颜色 设置为白色。具体设置可参见下图: 图二 Hello 窗体的属性设置 设置完成后,将其保存为 hello.ui 文件,这个文件就是 Hello 窗体的界面存储文件。 ③ 生成 Hello 窗体类的头文件和实现文件 下面我们根据上述的界面文件 hello.ui 使用 uic 工具产生出 Hello 窗体类的头文件和 实现文件,具体方法是: cd qt-2.3.7/bin uic –o hello.h hello.ui uic –o hello.cpp –impl hello.h hello.ui 这样我们就得到了 Hello 窗体类的头文件 hello.h 和实现文件 hello.cpp。下面我们就可以 根据我们要实现的具体功能,在 hello.cpp 文件里添加相应的代码。 比如我们要在 Hello 的窗体上显示一个动态的字符串“Hello,World”,那么我们需要 重新实现 paintEvent( QPaintEvent * )方法,同时我们还需要添加一个定时器 Qtimer 实 例,以周期性刷新屏幕,从而得到动画得效果。下面是我们修改后的 hello.h 和 hello.cpp 文件。 /**************************************************************************** ** 以下是 hello.h 的代码 ****************************************************************************/ #ifndef HELLO_H #define HELLO_H #include #include class QVBoxLayout; class QHBoxLayout; class QGridLayout; class Hello : public QWidget { Q_OBJECT public: Hello( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); ~Hello(); //以下是手动添加的代码 signals: void clicked(); protected: void mouseReleaseEvent( QMouseEvent * ); void paintEvent( QPaintEvent * ); private slots: void animate(); private: QString t; int b; }; #endif // HELLO_H /**************************************************************************** ** 以下是 hello.cpp 源代码 ****************************************************************************/ #include "hello.h" #include #include #include #include #include #include #include #include /* * Constructs a Hello which is a child of 'parent', with the * name 'name' and widget flags set to 'f' */ Hello::Hello( QWidget* parent, const char* name, WFlags fl ) : QWidget( parent, name, fl ) { if ( !name ) setName( "Hello" ); resize( 240, 320 ); setMinimumSize( QSize( 240, 320 ) ); setMaximumSize( QSize( 240, 320 ) ); setSizeIncrement( QSize( 240, 320 ) ); setBaseSize( QSize( 240, 320 ) ); QPalette pal; QColorGroup cg; cg.setColor( QColorGroup::Foreground, black ); cg.setColor( QColorGroup::Button, QColor( 192, 192, 192) ); cg.setColor( QColorGroup::Light, white ); cg.setColor( QColorGroup::Midlight, QColor( 223, 223, 223) ); cg.setColor( QColorGroup::Dark, QColor( 96, 96, 96) ); cg.setColor( QColorGroup::Mid, QColor( 128, 128, 128) ); cg.setColor( QColorGroup::Text, black ); cg.setColor( QColorGroup::BrightText, white ); cg.setColor( QColorGroup::ButtonText, black ); cg.setColor( QColorGroup::Base, white ); cg.setColor( QColorGroup::Background, white ); cg.setColor( QColorGroup::Shadow, black ); cg.setColor( QColorGroup::Highlight, black ); cg.setColor( QColorGroup::HighlightedText, white ); pal.setActive( cg ); cg.setColor( QColorGroup::Foreground, black ); cg.setColor( QColorGroup::Button, QColor( 192, 192, 192) ); cg.setColor( QColorGroup::Light, white ); cg.setColor( QColorGroup::Midlight, QColor( 220, 220, 220) ); cg.setColor( QColorGroup::Dark, QColor( 96, 96, 96) ); cg.setColor( QColorGroup::Mid, QColor( 128, 128, 128) ); cg.setColor( QColorGroup::Text, black ); cg.setColor( QColorGroup::BrightText, white ); cg.setColor( QColorGroup::ButtonText, black ); cg.setColor( QColorGroup::Base, white ); cg.setColor( QColorGroup::Background, white ); cg.setColor( QColorGroup::Shadow, black ); cg.setColor( QColorGroup::Highlight, black ); cg.setColor( QColorGroup::HighlightedText, white ); pal.setInactive( cg ); cg.setColor( QColorGroup::Foreground, QColor( 128, 128, 128) ); cg.setColor( QColorGroup::Button, QColor( 192, 192, 192) ); cg.setColor( QColorGroup::Light, white ); cg.setColor( QColorGroup::Midlight, QColor( 220, 220, 220) ); cg.setColor( QColorGroup::Dark, QColor( 96, 96, 96) ); cg.setColor( QColorGroup::Mid, QColor( 128, 128, 128) ); cg.setColor( QColorGroup::Text, black ); cg.setColor( QColorGroup::BrightText, white ); cg.setColor( QColorGroup::ButtonText, QColor( 128, 128, 128) ); cg.setColor( QColorGroup::Base, white ); cg.setColor( QColorGroup::Background, white ); cg.setColor( QColorGroup::Shadow, black ); cg.setColor( QColorGroup::Highlight, black ); cg.setColor( QColorGroup::HighlightedText, white ); pal.setDisabled( cg ); setPalette( pal ); QFont f( font() ); f.setFamily( "adobe-helvetica" ); f.setPointSize( 29 ); f.setBold( TRUE ); setFont( f ); setCaption( tr( "" ) ); //以下是手动添加的代码 t = "Hello,World"; b = 0; QTimer *timer = new QTimer(this); connect( timer, SIGNAL(timeout()), SLOT(animate()) ); timer->start( 40 ); } /* * Destroys the object and frees any allocated resources */ Hello::~Hello() { } /* 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 blt'ed to the screen. */ void Hello::paintEvent( QPaintEvent * ) { static int sin_tbl[16] = { 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 widget's 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 ( !t[i].isNull() ) { int i16 = (b+i) & 15; p.setPen( QColor((15-i16)*16,255,255,QColor::Hsv) ); p.drawText( x, y-sin_tbl[i16]*h/800, t.mid(i,1), 1 ); x += fm.width( t[i] ); i++; } p.end(); // 4: Copy the pixmap to the Hello widget bitBlt( this, pmx, pmy, &pm ); } ④ 编写主函数 main() 一个 Qt/Embeded 应用程序应该包含一个主函数,主函数所在的文件名是 main.cpp。主 函数是应用程序执行的入口点。以下是 Hello,World 例子的主函数文件 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. */ #define QT_NO_WIZARD int main( int argc, char **argv ) { QApplication a(argc,argv); Hello dlg; QObject::connect( &dlg, SIGNAL(clicked()), &a, SLOT(quit()) ); a.setMainWidget( &dlg ); dlg.show(); return a.exec(); } ⑤ 编辑工程文件 hello.pro 文件 到目前为止,我们为 Hello,World 例子编写了一个头文件和两个源文件,这 3 个文件应 该 被 包 括 在 工 程 文 件 中 , 因 此 我 们 需 要 编 辑 hello.pro 文 件 , 加 入 这 hello.h,hello.cpp,main.cpp 这三个文件名。具体定义如下 /**************************************************************************** ** 以下是 hello.pro 文件的内容 ****************************************************************************/ TEMPLATE CONFIG HEADERS SOURCES INTERFACES = app = qt warn_on release = hello.h = hello.cpp \ main.cpp = ⑥ 生成 Makefile 文件 编译器是根据 Makefile 文件内容来进行编译的,所以我们需要生成 Makefile 文件。Qt 提供的 tmake 工具可以帮助我们从一个工程文件(.pro 文件)中产生 Makefile 文件。结合 当前例子,我们要从 hello.pro 生成一个 Makefile 文件的做法是:首先查看环境变量 $TMAKEPATH 是否指向 arm 编译器的配置目录,在命令行下输入以下命令 echo $TMAKEPATH 如果返回的结果的末尾不是 。。。。/qws/linux-arm-g++ 的字符串,那你需要把环境变量 $TMAKEPATH 所指的目录设置为指向 arm 编译器的配置目录,过程如下, export TMAKEPATH = /tmake 安装路径/qws/linux-arm-g++ 同时,我们还要确保当前的 QTDIR 环境变量指向 Qt/Embedded 的安装路径,如果不是,则需 要执行以下过程 export QTDIR = ……/qt-2.3.7 上述步骤完成后,我们就可以使用 tmake 生成 Makefile 文件,具体做法是在命令行输入以 下命令: tmake –o Makefile hello.pro 这样我们就会看到当前目录下新生成了一个名为 Makefile 的文件。下一步,我们需要打开 这个文件,做一些小的修改。 (1)将 LINK = arm-linux-gcc 这句话改为 LINK = arm-linux-g++ 这样做是因为要是用 arm-linux-g++进行链接 (2)将 LIBS = $(SUBLIBS) -L$(QTDIR)/lib -lm –lqte 这句话改为 LIBS = $(SUBLIBS) -L/usr/local/arm/2.95.3/lib -L$(QTDIR)/lib -lm –lqte 这是因为链接时要用到交叉编译工具 toolchain 的库。 ⑦ 编译链接整个工程 最后我们就可以在命令行下输入 make 命令对整个工程进行编译链接了。 make make 生成的二进制文件 hello 就是可以在 FS2410 上运行的可执行文件。下面我们将介 绍如何将这个文件发布到 FS2410 上。 五、发布一个 Qt/Embeded 应用到 FS2410 在优龙公司的《FS2410 开发板使用手册》的第六章“Linux 的引导和烧写”介绍了如何 把 Linux 的引导和内核程序的映象文件烧写到 FS2410 的 FLASH 上。一个 Qt/Embedded 应用 的运行需要有 Linux 操作系统和 Qt/Embedded 库的支持。所以我们除了要烧写 Linux 到 FS2410 的 FLASH 存储空间之外,我们还要烧写 Qt/Embedded 的二进制库到 FS2410 的 FLASH。 一般来说,我们先把 Qt/Embedded 的二进制库复制到某个目录下,然后再把这个目录制成某 种类型的根文件系统,最后把这个根文件系统烧写到 FS2410 的 FLASH 上,这个过程可能需 要一些制作根文件系统的工具,例如 mkcramfs。在优龙公司的 FS2410 开发套件中附有的一 张光盘中,有一个叫 Linux 的文件夹,包含了一个名为 qtopia.cramfs 的根文件系统映象, 这个根文件系统包含了 trolltech 公司使用 Qt/Embedded 开发的一个 PDA 应用环境(简称 QPE),当用户把这个根文件系统映象烧写到 FS2410 的 FLASH 存储空间后,在 FS2410 的根文 件系统下就包含了一个 Qt/Embedded 2.3.7 版本的二进制库文件。 鉴于目前发行的套件中的 uClinux 均采用 ROMFS 作为其根文件系统,因此其目录大多是 不可写的。只有/var,/tmp 是 RAM 盘可写,但板子一掉电里面的内容就丢失了,因此只能 作临时文件保存,无法永久的保存数据,例如配置文件等。但是/var,/tmp 这些目录可作 为我们调试程序的安装目录,我们可以把编译好的 Qt/Embedded 应用程序传送到这些目录, 并运行。 下面我们简单介绍一下如何把一个 Qt/Embedded 应用程序传送到 uClinux 的根文件系统 的/tmp 目录。 ①用 FS2410 开发套间中的串口线连接好开发用的机器的串口和 FS2410 开发板的串口。 ② 在 WINDOWS 操作系统的程序|附件|通讯中运行一个超级终端,并设定通过串口 x 与 FS2410 进行连接。具体设置过程见下列图示: 图三 输入连接名称 图四 输入连接所使用的串口的端口号 图五 设置串口通讯的的参数 ③ 打开 FS2410 的电源,或者复位 FS2410,这时可在超级终端的窗口中看到 BIOS 引导信息, 如下图 ④待 Linux 引导完成后,在当前文件系统的根目录下,运行“cd tmp”命令,进入到 tmp 目录,如下图 ④ 在当前命令行下输入“rz”命令,开始一个文件传输过程;接着点击鼠标右键,在弹出 的菜单中,点击“发送文件”子菜单,然后选择你要传送到 tmp 目录的 Qt/Embedded 应 用程序。 例如,下图是正在传送 Hello,World 例子的可执行文件到 tmp 目录 ⑤ 最后我们就可以在 tmp 目录下运行传送过来的一个 Qt/Embedded 应用程序。 六、添加一个 Qt/Embedded 应用到 QPE 前面我们提到了优龙光盘里面有一个名为 qtopia.cramfs 的根文件系统映象,烧写这个 根文件系统到 FS2410 的 FLASH,就会安装有 QT PDA 应用环境(简称 QPE)。我们可以在 QPE 里添加我们自己编写的应用,不过添加的过程需要重新交叉编译 QPE 的源文件。下面我们介 绍如何在 QPE 里添加我们编写的 Hello,World 的例子。 ① 在工作的机器上解包 qtopia tar zxvf qtopia-free-1.7.x.tar.gz cd qtopia-free-1.7.x export QTDIR=$QTEDIR export QPEDIR=$PWD export PATH=$QPEDIR/bin:$PATH 注意在上面我们已经设定环境变量 QPEDIR 为 QPE 的安装(解包)路径。 ② 建立 Hello,World 的例子程序的图标文件 方 法 是 : 制 作 一 个 32 X 32 大 小 的 PNG 格 式 的 图 标 文 件 , 将 该 文 件 存 放 在 $QPEDIR/pics/inline 目录下,然后使用以下命令将$QPEDIR/pics/inline 目录下的所 有图形文件转换成为一个 c 语言的头文件,这个头文件包含了该目录下的图形文件的 rgb 信息。 qembed --images $QPEDIR/pics/inline/*.* > $QPEDIR/src/libraries/qtopia/inlinepics_p.h 注意上述的 qembed 是在 qt3.x.x for x11 上才发布有的工具 ③ 交叉编译 qtopia 在$QPEDIR 路径下,运行以下命令 cd src ./configure –platform linux-arm-g++ make cd .. ④ 建立应用启动器(.desktop)文件 方法是:建立一个文本文件,在文件中添加以下的内容,这些内容指明了应用的名称, 图标名等信息,然后将文件更名为 xxxx.desktop,保存在$QPEDIR/apps/applications 目录下。 以下是我们例子程序的启动器文件(hello.desktop): [Desktop Entry] Comment=A Hello Program Exec=hello Icon=Hello Type=Application Name=hello ⑥ 建立根文件系统 在这里我们利用原有的 qtopia.cramfs 的根文件系统映象,把我们新建的应用的相关文 件添加到这个根文件系统中。首先我们先要把 qtopia.cramfs 的根文件系统 mount 到我们工 作机器上来,然后复制这个文件系统的内容到一个临时的 temp 目录,这时我们可以在 temp\Qtopia 目录下看到一个 qtopia-free-1.7.0 目录,这就是 qtopia.cramfs 的根文件系 统里的 qpe 安装目录,接着把我们的新建的应用的相关文件(包括启动器文件,包含了图标 的库文件 libqte.so.*,和应用程序的可执行文件)复制到 temp/Qtopia/ qtopia-free-1.7.0 的对应的目录。具体过程如下 mkdir /mnt/cram mount –t cramfs qtopia.cramfs /mnt/cram –o loop cp –ra /mnt/cram temp/ cp $QPEDIR/apps/Applications/hello.desktop (接下行) temp/Qtopia/qtopia-free-1.7.0/apps/Applications/ cp $QPEDIR/lib/libqpe.so.* (接下行) temp/Qtopia/qtopia-free-1.7.0/lib/ cp hello temp/Qtopia/qtopia-free-1.7.0/bin (拷贝可执行文件) mkcramfs temp xxxxxx.cramfs (生成新的根文件系统) 将生成的新的根文件系统烧写到 FS2410 的 FLASH 根文件系统区,复位,OK,就可以看到 QPE 里有我们编写的应用的图标了,点击这个图标,程序就成功运行了。当然为了使我们的应用 和原先的 QPE 的应用具备统一的界面风格,我们在编写我们自己的应用的主函数文件 (main.cpp)时,不妨使用 QPE 提供的宏,具体可参考 QPE 应用程序的源文件。下面是我们 编写的 Hello,world 程序在 qvfb 的截屏图。 小记 开发 Qt/Embedded 应用对初学者可能是一项艰苦的工作过程,因为需要安装和设置很多 的内容,有时候某一过程没有进行可能会导致一些莫名奇妙的出错提示。尽管我们的开发文 档详细的介绍了嵌入式 Qt 的开发过程,然而我们还是不能保证初学者一步一步按照我们所 描述的去做便可以在编译应用时万无一失,因为 linux 开发包之间有一定的依赖性,这些开 发包又从属不同的开发商或组织。我们的建议是您在您的机器上安装 linux 并准备进行开发 时,至少要安装一个工作站版的 linux。您需要注意您的机器上安装有 libjpeg, e2fsprogs, Freetypes 等开发包有否安装,因为上述我们谈到的 Qt 嵌入式的软件都用到了这些开发包。 您不必为这些繁琐的工作感到沮丧,因为这些工作会让你有机会认识嵌入式软件工作的底层 细节。您会把嵌入式软件的开发过程当作是在玩一个锻炼智力的玩具吗?您很快会有喜欢上 这种玩具的感觉的!

    Top_arrow
    回到顶部
    EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。