分类: LINUX
2013-08-28 19:21:07
原文地址:arm qt移植全程详解 作者:wpneu
Qt是一个跨平台的C++图形用户界面库,由挪威TrollTech公司()出品,它的目的是提供开发应用程序用户界面部分所需要的一切,主要通过汇集C++类的形式来实现这一目的。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的,很容易扩展的,并且允许真正地组件编程的GUI开发工具。
QTE(QT Embedded)是它其中的一个版本,适用于嵌入式系统,QTE直接基于Linux中的FrameBuffer设备。
QPE(Qt
Plamtop Environment)是Trolltech公司所推出的针对PDA软件的整体解决方案,包含了从底层的GUI系统、Window Manager、Soft
Keyboard到上层的PIM、浏览器、多媒体等方面。目前QPE的高版本已更名为Qtopia,其包含了更多功能。
Qt/Embedded 简介
Qt/Embedded 是一个为嵌入式设备上的图形用户接口和应用开发而订做的C++工具开发包。它通常可以运行在多种不同的处理器上部署的嵌入式Linux操作系统上。居于Qt/Embedded的应用程序可以直接对缓冲帧进行写操作。使用标准的Qt API,我们可以非常熟练的在Windows和Unix编程环境里开发应用程序。本文介绍的QTE基于qt-embedded-
Qtopia 是Trolltech为采用嵌入式Linux操作系统的消费电子设备而开发的综合应用平台, Qtopia包含完整的应用层、灵活的用户界面、窗口操作系统、应用程序的启动程序以及开发框架。
Trolltech提供三大Qtopia 版本:Qtopia手机版、Qtopia PDA 版和Qtopia 消费电子产品平台,本文介绍的是Qtopia PDA 版,基于qtopia-free-
Qt/Embedded 是一个为嵌入式设备上的图形用户接口和应用开发而订做的C++工具开发包。它通常可以运行在多种不同的处理器上部署的嵌入式Linux操作系统上。如果不考虑X Window窗口系统的需要,居于Qt/Embedded的应用程序可以直接对缓冲帧进行写操作。除了类库以外,Qt/Embedded还包括了几个提高开发速度的工具,使用标准的Qt API,我们可以非常熟练的在Windows和Unix编程环境里开发应用程序。
HOST环境:
系统:Fedora 8
交叉编译工具arm 2.95.3
TARGET环境:
基于pxa270处理器的CPB6902板卡
320*240LCD屏
内核:Linux-
文件系统:RAMDISK
软件:
qt-embeded-
qt-x11-
qtopia-free-src-
e2fsprogs-1.39.tar.gz
jpegsrc.v6b.tar.gz
libpng-
tslib-1.3.tar.bz2
zlib-
建立编译目录:
mkdir –p /home/work/armqt/qtlibsource
解压以上软件分别得到:
qt-x11,tmake-1.13, qt-
安装顺序:先安装qt-x11-
安装qt-x11-
· tmake1.13 或更高版本:生成Qt/Embedded应用工程的Makefile文件。
· Qt/Embedded:Qt/Embedded安装包。
· Qt
1) 编译qt-x11,得到本机运行工具uic,designer,qvfb
export TMAKEDIR=/home/work/armqt/qtlibsource /tmake-1.13
export TMAKEPATH=/home/work/armqt/qtlibsource//tmake-1.13/lib/linux-g++
export QTDIR=/home/work/armqt/qtlibsource/temp/qt-x11
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
编译可能遇到的问题:
a)
/home/work/armqt/qtlibsource/qt-x11/include/qvaluestack.h:
In member function ‘T QValueStack
xml/qxml.cpp:513: instantiated from here
/home/work/armqt/qtlibsource/qt-x11/include/qvaluestack.h:57:
错误:不能从 ‘QValueListIterator
/home/work/armqt/qtlibsource/qt-x11/include/qvaluestack.h:
In member function ‘T QValueStack
xml/qxml.cpp:2502: instantiated from here
/home/work/armqt/qtlibsource/qt-x11/include/qvaluestack.h:57:
错误:不能从 ‘QValueListIterator
解决办法:vi include/qvaluestack.h
第57行修正如下:
remove( this->fromLast() );---àthis->remove( this->fromLast() );
2) 编译Qt/Embedded
export
QTDIR=/home/work/armqt/qtlibsource/temp/qt-
export QTEDIR=$QTDIR
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure –xplatform linux-arm-g++ -shared -qvfb -depths 4,8,16,32
make
make install
编译遇到的问题:
a)
/home/work/armqt/qtlibsource/temp/qt-
/home/work/armqt/qtlibsource/temp/qt-
解决方法:
vi include/qsortedlist.h
第51行修正如下:
~QSortedList() { clear(); }--à~QSortedList() { this->clear(); }
b)
/home/work/armqt/qtlibsource/temp/qt-
解决发法:
vi include/qwindowsystem_qws.h
第227增加:
#ifndef QT_NO_QWS_IM
static void setCurrentInputMethod( QWSInputMethod *im );
---à
#ifndef QT_NO_QWS_IM
class QWSInputMethod;
static void setCurrentInputMethod( QWSInputMethod *im );
c)
kernel/qgfxvfb_qws.cpp:143: 错误:‘is_screen_gfx’ 在此作用域中尚未声明
kernel/qgfxvfb_qws.cpp:144: 错误:‘xoffs’ 在此作用域中尚未声明
kernel/qgfxvfb_qws.cpp:144: 错误:‘yoffs’ 在此作用域中尚未声明
……..
kernel/qgfxtransformed_qws.cpp:674: 错误:‘xoffs’ 在此作用域中尚未声明
kernel/qgfxtransformed_qws.cpp:674: 错误:‘yoffs’ 在此作用域中尚未声明
……
解决办法:
修改相应文件,在所有未声明变量前加this->
3)编译qtopia-free-
建立目录include,lib, 这两个目录是存放编译后的库与头文件,为后面交叉编译用)
mkdir –p /home/work/armqt/lib
mkdir –p /home/work/armqt/include
编译相关库
(1) e2fs:
cd home/work/armqt/qtlibsource/e2fsprogs-1.39
./configure --host=arm-linux
--enable-elf-shlibs --with-cc=arm-linux-gcc
--with-linker=arm-linux-ld
--prefix=/usr/local/arm/2.95.3/arm-linux
make
make install
cp lib/libuuid.so* ../lib
(2)
jpeg-6b
cd /home/work/armqt/qtlibsource/jpeg-6b
./configure -enable-shared
vi Makefile
修改:
CC= arm-linux-gcc
AR= arm-linux-ar rc
AR2=arm-linux-ranlib
make
make install
cp *.h ../include/
cp libjpeg.a ../lib/
(3)
zlib-
cd /home/work/armqt/qtlibsource/zlib-
./configure -shared
Vi Makefile
CC=arm-linux-gcc
…
LDSHARED=arm-linux-gcc -shared -Wl,-soname,libz.so.1
CPP=arm-linux-gcc -E
…
AR=arm-linux-ar rc
RANLIB=arm-linux-ranlib
…
SHELL=/bin/sh
EXE=
prefix =/usr/local/arm/2.95.3/arm-linux
…
make
make install
cp libz.so* ../lib/
cp *.h ../include/
(4)
libpng-
cp scripts/makefile.linux ./Makefile
vi Makefile
AR_RC=arm-linux-ar rc
CC=arm-linux-gcc
…
RANLIB=arm-linux-ranlib
…
prefix=/usr/local/arm/2.95.3/arm-linux
…
make
make install
cp libpng12.so* ../lib/
cp libpng12.so ../lib/libpng.so
cp *.h ../include/
(5)
tslib-1.3 (在我们的开发平台上由于还没有触摸屏驱动,使用的是usb鼠标,所以暂时没有使用此库,后面不再叙述关于触摸屏的相关内容)
cd /home/work/armqt/qtlibsource/tslib-1.3
./autogen.sh
echo"ac_cv_func_malloc_0_nonnull=yes"
>arm-linux.cache
./configure
--host=arm-linux--cache-file=arm-linux.cache
--enable-inputapi=no --srcdir=/home/qtopia-arm-home/arm/tslib
make
make install
cp src/.libs/libts-0.0.so.0*
../lib/
cp src/.libs/libts.so ../lib/
cp src/*.h ../include/
(6)编译Qtopia
cd /home/work/armqt/ qtlibsource /qtopia-free-
(a)修改相关文件:
vi /home/work/armqt/qtlibsource/qtopia-free-
linux-arm-g++/qmake.conf
将
QMAKE_LIBS_QT = -lqte
修改为:
QMAKE_LIBS_QT = -lqte -lpng -lz -luuid -ljpeg
让Qtopia支持鼠标,修改文件:
vi home/work/armqt/qtlibsource/qtopia-free-
注释如下宏:
/*
#ifndef QT_NO_QWS_CURSOR
#define QT_NO_QWS_CURSOR
#endif
#ifndef QT_NO_QWS_MOUSE_AUTO
#define QT_NO_QWS_MOUSE_AUTO
#endif
#ifndef QT_NO_QWS_MOUSE_PC
#define QT_NO_QWS_MOUSE_PC
#endif
*/
(b)设置环境变量:
export QTDIR=/home/work/armqt/qtlibsource/qtopia-free-
export QPEDIR=/home/work/armqt/
qtlibsource/qtopia-free-
export
LD_LIBRARY_PATH=$QTDIR/lib:$QPEDIR/lib:$LD_LIBRARY_PATH
export TMAKEDIR=/home/work/armqt/qtlibsource/qtopia-free-
export
TMAKEPATH=$TMAKEDIR/lib/qws/linux-arm-g++
(c)准备配置文件:
cp $QPEDIR/src/qt/qconfig-qpe.h $QTDIR/src/tools
cd $QPEDIR/src/libraries/qtopia
cp custom-linux-ipaq-g++.cpp custom-linux-arm-g++.cpp
cp custom-linux-ipaq-g++.h custom-linux-arm-g++.h
(d)生成Makefile文件
./configure -qte "-embedded -xplatform linux-arm-g++
-qconfig qpe
-no-qvfb -depths 8,16,24,32
-no-xft -system-jpeg
-system-libpng
-system-zlib -gif -thread -release
-I/home/work/armqt/include
-L/home/work/armqt/lib -lpng -lz -luuid -ljpeg"
-qpe '-xplatform
linux-arm-g++ -edition pda -displaysize 640x480
-I/home/work/armqt/include
-L/home/work/armqt/lib
-prefix=/home/work/qtopia'
make
make install
最后在/home/work/目录下生成了一个文件夹为qtopia的Qt文件系统
编译过程出现的问题
1)
……/qtopia-free-
解决方法:
vi qt2/include/qvaluestack.h
第57行
remove( this->fromLast() );-à
this->remove(this->fromLast() );
2)
../../libraries/qtopia/qdawg.cpp:294:
错误:有多余的限定 ‘QDawgPrivate::’ 在成员 ‘QDawgPrivate’ 上
make[6]: ***
[.obj/release-shared/qdawg.o] 错误 1
make[5]: *** [all] 错误 2
make[4]: *** [sub-tools-qdawggen]
错误 2
make[3]: ***
[sub-src-components_pro] 错误 2
make[2]: *** [all] 错误 2
make[1]: *** [all] 错误 2
解决办法:
vi qtopia/src/libraries/qtopia/qdawg.cpp
第294行:
QDawgPrivate::~QDawgPrivate()-à ~QDawgPrivate()
(e)拷贝相关库
将前面准备工作编译的相关库文件考入到Qt文件系统中
cp /home/work/armqt/lib/* /home/work/qtopia/lib
(7)在开发板上运行
由于我们的根文件系统只有4M,无法容纳生成的Qt文件系统,所以这里采用NFS的方式来运行。
主机Qtopia文件系统路径如下:
/home/project/pxa270/rootfs/qtopia
我们将qtopia挂载到开发板的/mnt/gui目录下:
mkdir /mnt/gui
mount -t nfs -o intr,nolock,rsize=1024,wsize=1024
192.168.0.170:/home/project/pxa270/rootfs /mnt/gui
(如果不加入rsize、wsize参数会出现nfs 没有响应的问题)
在qtopia下可能没有pointercal文件,如果没有可能会导致只能看到qt的初始界面,出现一个对话框:calibration can only be performed on a touch screen
点击始终无法进入qt。
可以在/etc目录下创建一个内容为 1 0 1 0 1
1 65536的pointercal文件解决。
消除Warning: Unable to open /usr/share/zoneinfo/zone.tab
在目标机上建立目录mkdir –p /usr/share/zoneinfo
拷贝主机目录文件/qtopia-free-
设置板子上QT的运行环境变量:
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/mnt/gui/qtopia/bin
export LD_LIBRARY_PATH=/lib:/usr/lib:/mnt/gui/qtopia/lib
export LANG="zh"_CN
export QWS_MOUSE_PROTO="USB:/dev/input/mice"(指定鼠标为输入设备)
export QTDIR="/mnt/gui/qtopia"
export QPEDIR="/mnt/gui/qtopia"
运行:
cd /mnt/gui/qtopia/bin
./qpe
就可以看到漂亮的qtopia界面了。
前面我们在主机上编译安装了Qt/Embeded工具开发包,现在就可以利用此开发包进行QT程序的编写了,下面以编写一个Hello,QT/Embeded为例来演示如何开发并在发布一个QT应用程序。
一个应用通常对应一个工程文件,生成一个工程文件,并对它做一些简单的编辑,然后
使用一个专门的工具(例如 tmake)处理这个工程文件,就可以生成一个 Makefile文件。
产生一个工程文件的其中一个方法是使用 progen 命令(progen 程序可在tmake 的安装路径下找到)。 下面使用 progen 产生一个名为 hello 的工程文件
在/etc/profile中加入相关工具的路径信息:
pathmunge /home/work/armqt/qtlibsource/tmake-1.13/bin
pathmunge /home/work/armqt/qtlibsource/qt-
pathmunge /home/work/armqt/qtopia-free-
pathmunge /home/work/armqt/qtlibsource/qt-x11/bin (包含designer工具)
1.生成hello.pro工程文件:
progen –t app.t –o hello.pro
2.编写一个hello.cpp
#include
#include
#include
#include
int main( int argc, char **argv )
{
QApplication app( argc, argv );
QPushButton hello( QPushButton::tr("Hello,world!"), 0 );
hello.setGeometry( 20, 20, 180, 100 );
app.setMainWidget( &hello );
hello.show();
return app.exec();
}
3.在.pro文件中加入hello.cpp
TEMPLATE = app
CONFIG = qt warn_on release
HEADERS =
SOURCES = hello.cpp
INTERFACES =
4.生成Makefile文件
配置环境变量
export $TMAKPATH= /home/work/armqt/qtlibsource/tmake-1.13/lib/qws/linux-arm-g++
export
$QTDIR=/home/work/armqt/qtlibsource/qt-
tmake -o Makefile hello.pro
修改Makefile
1):LINK =
arm-linux-g++
2):LIBS = $(SUBLIBS) -L/usr/local/arm/2.95.3/lib -L$(QTDIR)/lib
-lm -lqte
make
就可以生成一个交叉编译环境的可执行文件hello了。
1.建立应用启动器(.desktop文件)
在板子上的目录/mnt/gui/qtopia/apps/Applications下 建立一个hello.desktop文件,添加如下内容:
[Translation]
File=QtopiaApplications
Context=Hello
[Desktop Entry]
Comment[]=A Hello Program
Exec=hello
Icon=hello
Type=Application
Name[]=Hello
这些内容指明了应用的名称,图标等信息。
2.添加hello应用程序的图标
在/mnt/gui/qtopia/pics/目录下:
mkdir hello
添加我们喜欢的应用程序图标到此目录(利用NFS拷贝)
如hello.png
2) 拷贝前面交叉编译好的可执行文件hello到如下目录(利用NFS拷贝):
/mnt/gui/qtopia/bin
然后在开发板上运行qtopia:
./qpe
进入到Application界面,我们就可以看到一个图标为hello的应用程序,点击该程序,就可以看到我们的第一个Hello,world应用程序了。
这里我们以开发一个中文显示的QT下拉菜单程序,中文的显示我们是利用在程序中需要汉化的地方用tr()方法标识,然后利用lupdate,lrelease和lingust工具,生成.ts和.qm等i18n需要的文件;这三个工具我们可以在/home/work/armqt/qtopia-free-
QT中文程序的制作步骤如下:
1)编写并编译应用程序
2)在.pro文件中加入TRANSLATIONS = XXX_CN.ts
3)lupdate 利用.pro文件生成相应的.ts文件(此文件为一个xml文件,含有我们需要翻译的源文字)
4)用lingust对生成的.ts文件进行翻译
5)利用lrelease 结合.ts文件生成.qm文件
6)发布可执行文件和.qm文件。
1.
编写下拉菜单程序
mainwidget.h
#include
#include
#include
#include
#include
#include
class MyMainWidget: public QMainWindow
{
Q_OBJECT
public:
MyMainWidget( QWidget *parent=0, const char *name=0 );
public slots:
void newFile();
void openFile();
void saveFile();
void exitMain();
void copyFile();
private:
QLabel *statuslabel;
};
#endif
//如下(加粗)可以看到在我们需要汉化的地方都用tr()方法进行标识,利用lupdate就可提取出其中需要汉化的源文字,从而生成.ts文件。
mainwidget.cpp
#include "mainwidget.h"
MyMainWidget::MyMainWidget( QWidget *parent, const char *name )
:QMainWindow( parent, name )
{
setCaption(MyMainWidget::tr("chinese
qt Example"));
setBackgroundColor( white );
statuslabel = new QLabel( "", this );
statuslabel->setGeometry( 50, 50, 250, 50 );
statuslabel->setBackgroundColor( white );
QPopupMenu *filemenu1 = new QPopupMenu;
filemenu1->insertItem(QPopupMenu::tr("&New"),
this, SLOT( newFile() ),CTRL+Key_N );
filemenu1->insertItem(QPopupMenu::tr("&Open"), this,
SLOT( openFile() ),CTRL+Key_O );
filemenu1->insertItem(QPopupMenu::tr("&Save"),
this, SLOT( saveFile() ), CTRL+Key_S );
filemenu1->insertSeparator();
filemenu1->insertItem(QPopupMenu::tr("E&xit"), this,
SLOT( exitMain() ),CTRL+Key_X );
QPopupMenu *filemenu2 = new QPopupMenu;
filemenu2->insertItem(QPopupMenu::tr("&Copy"),
this, SLOT( copyFile() ),CTRL+Key_C );
QMenuBar *menubar;
menubar = new QMenuBar( this );
menubar->insertItem(QMenuBar::tr("&File"),
filemenu1);
menubar->insertItem(QMenuBar::tr("&Edit"), filemenu2);
statusBar()->message(
MyMainWidget::tr("Ready"));
}
void MyMainWidget::newFile()
{
statuslabel->setText(
MyMainWidget::tr("File has been created!") );
statusBar()->clear();
statusBar()->message(
MyMainWidget::tr("created") );
}
void MyMainWidget::openFile()
{
statuslabel->setText(
MyMainWidget::tr("File has been opened!") );
statusBar()->clear();
statusBar()->message(
MyMainWidget::tr("Opened") );
}
void MyMainWidget::saveFile()
{
statuslabel->setText(
MyMainWidget::tr("File has been saved!") );
statusBar()->clear();
statusBar()->message( MyMainWidget::tr("Saved") );
}
void MyMainWidget::exitMain()
{
QApplication::exit();
}
void MyMainWidget::copyFile()
{
statuslabel->setText(
MyMainWidget::tr("File has been copyed!") );
statusBar()->clear();
statusBar()->message(
MyMainWidget::tr("copyed") );
}
main.cpp
#include
#include "mainwidget.h"
int main(int argc, char **argv)
{
QApplication app(argc,argv);
app.setFont(QFont("unifont",16,50));
QTranslator translator( 0 );
translator.load(
QString("hello_CN.qm"),".");
app.installTranslator( &translator
);
MyMainWidget *mymainwidget = new MyMainWidget(0);
mymainwidget->setGeometry(10,30,280,200);
app.setMainWidget(mymainwidget);
mymainwidget->show();
return app.exec();
}
2.
生成mymain.pro工程文件
progen –t app.t –o mymain.pro
修改mymain.pro
TEMPLATE = app
CONFIG = qt warn_on release
HEADERS = mainwidget.h
SOURCES = mainwidget.cpp main.cpp
INTERFACES =
TRANSLATIONS = mymain_CN.ts
3.
生成mymain_CN.ts
lupdate mymain.pro
4. 翻译my_CN.ts中提取的源文字。
5 生成.qm文件
lrelease my_CN.ts
root@localhost hellotest]# ls
mymain mymain_CN.ts mymain.pro main.o mainwidget.h Makefile moc_mainwidget.o mymain_CN.qm main.cpp mainwidget.cpp mainwidget.o moc_mainwidget.cpp
那么程序是如何找到这个源文字对应的译文呢?
可以看到在main.cpp中如下代码:
int main(int argc, char **argv)
{
……
app.setFont(QFont("unifont",16,50));
QTranslator translator( 0 );
translator.load( QString("mymain_CN.qm"),".");
app.installTranslator( &translator
);
……
}
这里我们使用了qtopia中自带的一种字体unifont,如何想要qt支持更加丰富的字体,我们可以自己添加字库。
6 按照3.3章节中的方法添加mymain到应用到Qtopia中,记得需要将mymain_CN.qm文件一起放到/mnt/gui/qtopia/bin文件夹下。
运行./qpe
点击mymain应用程序,就可以看到中文的下拉工具菜单了。
注意:如果你设置的字体QFont("unifont",16,50)中有一个参数不正确就会导致显示出方块。
7制作中文字库
程序是如何去寻找需要的字库呢?
我们可以在QT文件系统目录下:/mnt/gui/qtopia/lib/fonts中找到qt自带的字库文件,
cd /mnt/gui/qtopia/lib/fonts
ls
unifont.bdf unifont_160_50_t5.qpf
helvetica_100_50.qpf helvetica_240_75_t15.qpf
helvetica_100_50_t10.qpf helvetica_240_75_t5.qpf
……
同时该目录下还有一个fontdir文件:
vi fontdir
……
helvetica helvR18.bdf BDF n 50 180 u
helvetica helvR24.bdf BDF n 50 240 u
# Unifont is available in source form from
unifont
unifont.bdf BDF n 50 160 u
babelfish babelfish.ttf FT n 50 0 s
smallsmooth verdana.ttf FT n 50 0 s 90
……
每一行是关于一种字体的设置,对应列的定义如下:
<字体名称> <字体文件名> <字体渲染类型> <是否斜体> <是否粗体> <尺寸> <字体标志> [尺寸列表]
其中,
<字体渲染类型>:可以为TTF,BDF,QPF三种类型;
<是否斜体>:y表示为斜体,n表示正常体;
<是否粗体>:50表示正常体,75表示粗体;
<标志>:a使用ASCII字符集,u使用unicode字符集,s使用锯齿平滑(anti-aliased);
<尺寸>:0,则系统从[尺寸列表]中提取可以转换的字体尺寸;
我们上面的中文下拉菜单程序就是利用qt自带的一种中文字体unifont,程序会读取/mnt/gui/qtopia/lib/fonts/fontdir文件,并匹配相应的字体,如果在程序中QFont("unifont",16,50)中参数设置不正确,就无法匹配到相关字体,所以自然就显示出了方块了。(同时字体标志一定要加上u,说明使用unicode字符集,否则也会出现方块)
知道如何读取中文字库文件,我们就可以制作自己喜欢的中文字库并显示了。