2013年(50)
分类: LINUX
2013-04-22 08:03:59
------------------------------------------------
本文系本站原创,欢迎转载!
Qt是一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展,并且 允许真正地组件编程。
(一)首先我们来讲下我们安装qt的原因:
我们平常在编写程序用的最多的是什么?
也许你会答些代码,这个当然是,但是有一个很重要的东西是
使用#include
我们为什么使用include,因为我们想使用printf,fork,malloc等这些函数,
其实说白了就是使用别人写好的东西,以减少自己的代码量,这就是库的作用。
其实我们安装qt其实就是安装库及头文件。
当然如果我们想要更好的开发环境,就会希望有像vc那种可视化的开发工具,这些都是qt安装时生成,这也是为什么需要安装qt的原因。
(二)下面就进一步讲下一些概念:
首先让我们来了解下一些概念:
qt-x11
qt-embeded
qtopia
qt由于是跨平台的,故其能够在linux,window,mac等操作系统下运行。
既然是跨平台的,它就要将GUI分层,分成和平台相关的,及同平台无关的,所以当我们要安装qt时,就要针对具体平台下载具体平台的qt.
在Linux,由于Linux广泛使用的是xwindow协议,所以qt-x11也要依赖xwindow,这样当我们要在linux pc上安装qt时,就要有xlib的相关库,也许你会说ubuntu的桌面是gnome,而gnome是基于gtk,而gtk又是在glib上扩展,glib又是xlib封装,那ubuntu应该就有xlib的相关库了吧,非也,glib是glib,xlib是xlib,他们的接口都不一样,你让qt怎么使用xwindow的相关东西(函数),也许你还会说那为啥qt不直接使用glib,而是使用xlib,其实这个很好理解,因为qt其实是和gtk等价的东西,你让他 gtk使用一样的继承关系glib,那未免不好,再说xlib是最基本的基础,用他可以更好的实现跨平台,否则,就算是在Linux下,如果一个用gtk,一个用xwindox,那么qt都要进行改动。
Qt-embeded大家光看单词就可看出是指嵌入式qt,其实说是嵌入式,也说大了,只能说是主要针对具有framebuffer的嵌入式操作系统,如果是其他操作系统,你要修改的东西就多了去了,那还不如使用更精炼一点的gui,像minigui等,不过后来的版本的支持性更好,可以支持更多的图形引擎。既然提framebuffer,大家也猜到了,如果再缩小范围,其实这个所谓的嵌入式就是指嵌入式中的linux,但是它为什么要分出个pc linux及嵌入式linux版本,主要是因为嵌入式系统中资源有限,使用framebuffer能更有效的使用资源,且开发简单很多。既然是嵌入式,它也就没使用xlib了,因为xwindow当初就是为了适用一台服务器,多个终端,然后客户窗口通过网络同服务器窗口通信并被管理,这个在嵌入式系统中根本用不上,所以就没使用xlib,当然可能还有很多其他原因,但是嵌入式版本使用的窗口管理系统还是c/s模式。
Qtopia又是什么呢?qtopia其实可以看成是qt-embedded的扩展,他主要是实现了一个桌面,使得应用程序员开发更简单,同时也增加了一些组件。如果你使用qt开发一个桌面系统,你就要写一个应用程序,这个应用程序要实现像任务栏,桌面图标,ime像输入法顶层窗口,及应用管理的这些功能。而这些在qtopia中,他已经帮你实现了,你要做得是开发具体的应用程序,这样就为我们减少了很大的任务量。
(三)下面就进入qt安装。
(1)首先安装x11。
刚才说了x11版本使用的是xlib底层窗体机制,所以我们要确保x11 lib存在,然后我们就可以直接编译qt/x11,运行。
环境:PC所用 Linux系统版本:Ubuntu8.04
首先下载:
Qt/Embedded版本:qt- embedded-linux-opensource-src-
Qt/X11版本:qt-x11-opensource-src-
然后解压:
tar zxvf qt-x11-opensource-src-
cd ~/opt/qt
刚才说到在linux pc上的qt是基于xlib所以要保证xlib库的存在,如果不存在
使用sudo apt-get install libx11-dev
下载xlib库,同时我在编译的过程中也发现还需要xext库,也将他下载sudo apt-get install libxext-dev反正是在编译的过程中,发现什么错误,就看下,如果是缺少库,下载就是了。
然后配置。
由于是编译在pc上运行的库,配置非常简单
1 ./configure -prefix ~/opt/run/qt
2 make
3 make install
第1条命令是做啥用的呢,他其实就是生成makefile文件,及生成用来编译qt源代码用的一些工具,象qmake,qmake是用来解释.pro文件,然后生成makefile文件的,其实他的作用像automake,就是自动生成makefile文件。
第二条命令就是生成lib,及其他工具像designer,uic,等工具的
make install就是将lib,tool,doc复制到-prefix指定的文件夹。
2 make过程非常长,大概1-2小时,此时可以看看网页或出去玩下。
然后我们就可以测试了。
首先我们要添加环境变量,我们新建一个文件用来添加这些环境变量。
jimmy@jimmy-linux:~/opt/run/qt$ cat setenv.sh
export QTDIR=$HOME/opt/run/qt //使的qmake生成makefile知道include,lib的
路径,同时还有mkspecs规则
export QTDEDIR=$QTDIR
export PATH=$QTDIR/bin:$PATH //这使得qmake,uci等工具能够直接使用,而不用进入~/opt/run/qte
export LD_LIBRARY_PAHT=$QTDIR/lib:$LD_LIBRARY_PAHT
//这个使得加载程序时能够正确找到qt库
jimmy@jimmy-linux:~/opt/run/qt$ source setenv.sh
//source的作用是使得上面的设置生效。
上面的注释不在setenv.sh文件里,这里只是为了说明。
然后自己写个简单的程序。
1.#include
2 #include
3 int main( int argc, char **argv )
4 {
5 QApplication a( argc, argv );
6 QPushButton hello( "Hello world!", 0 );
7 hello.resize( 100, 30 );
9 hello.show();
10 return a.exec();
11 }
jimmy@jimmy-linux:~/test/cppd$ ls
testb test.cpp
jimmy@jimmy-linux:~/test/cppd$ qmake -project
jimmy@jimmy-linux:~/test/cppd$ ls
cppd.pro testb test.cpp
jimmy@jimmy-linux:~/test/cppd$ qmake
jimmy@jimmy-linux:~/test/cppd$ ls
cppd.pro Makefile testb test.cpp
jimmy@jimmy-linux:~/test/cppd$ make
g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I../../opt/run/qt/mkspecs/linux-g++ -
g++ -Wl,-rpath,/home/jimmy/opt/run/qt/lib -o cppd test.o -L/home/jimmy/opt/run/qt/lib -lQtGui -L/home/jimmy/opt/run/qt/lib -L/usr/X11R6/lib -lXext -lX11 -lQtCore -lm -lrt -ldl -lpthread
jimmy@jimmy-linux:~/test/cppd$ ls
cppd cppd.pro Makefile testb test.cpp test.o
jimmy@jimmy-linux:~/test/cppd$
.cppd
然后编译:
qt-embedded版本
过程一样,只是configure要变
我们首先编译一个在仿framebuffer的版本,即生成的代码是针对pc的,用qvfb来模仿framebuffer,达到在宿机上开发目标机的程序并调试,这样当在宿机上运行成功时,再用交叉编译工具重新编译即可。
.configure -prefix ~/run/qte -embedded x86 -qvfb
这样这个版本的qt就是以qvfb作为自己的显示设备。
然后make,make install
同样我么调试,由于这次我们要是使用qt/embedded,所以就要使用qt/embedded的库,其实只需修改
QTDIR等环境变量即可。
jimmy@jimmy-linux:~/opt/run/qte$ cat setenv.sh
export QTDIR=$HOME/opt/run/qte
//使的qmake生成makefile知道include,lib的
路径,同时还有mkspecs规则
export QTDEDIR=$QTDIR
export PATH=$QTDIR/bin:$PATH //这使得qmake,uci等工具能够直接使用,而不用进入 ~/opt/run/qte
export LD_LIBRARY_PAHT=$QTDIR/lib:$LD_LIBRARY_PAHT
//这个使得加载程序时能够正确找到qt库
jimmy@jimmy-linux:~/opt/run/qte$ source setenv.sh
//source的作用是使得上面的设置生效。
x11时上面的目录是qt,现在是qte了。
然后我们再进入test目录,编译test.cpp
jimmy@jimmy-linux:~/test/cppd$ make clean
rm -f test.o
rm -f *~ core *.core
jimmy@jimmy-linux:~/test/cppd$ make
g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_SHARED -I../../opt/run/qte/mkspecs/qws/linux-x86-g++ -
g++ -Wl,-rpath,/home/jimmy/opt/run/qte/lib -o cppd test.o -L/home/jimmy/opt/run/qte/lib -lQtGui -L/home/jimmy/opt/run/qte/lib -lQtNetwork -lQtCore -lm -lrt -ldl -lpthread
jimmy@jimmy-linux:~/test/cppd$ ./cppd
QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录
QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录
QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录
QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录
QWSSocket::connectToLocalFile could not connect:: 没有该文件或目录
jimmy@jimmy-linux:~/test/cppd$
这时运行./cppd出错了,为什么,这就要提到我们前面说的,embedded版本下的qt也是采用c/s模式,所以必须要有server,由于没有server就出现了上面的错误,这时可能会问那为什么x11版本下没有server就可以运行呢。其实x11版本下也是有server,因为我们的pc linux gnome桌面系统也是基于xwindow的,所以在启动时就启动xwindow的服务器,这个就是X,正因为有了这个Xserver,x11版本下执行./cppd就可以了。
jimmy@jimmy-linux:~$ ps -aux |grep X
Warning: bad ps syntax, perhaps a bogus '-'? See
root 5902 5.2 4.9 109168 100060 tty7 Rs+ 08:50 3:56 /usr/bin/X :0 -br -audit 0 -auth /var/lib/gdm/:0.Xauth -nolisten tcp vt7
jimmy 9182 0.0 0.0 3224
jimmy@jimmy-linux:~$
如果此时你sudo kill 5902,你将发现整个窗口程序没有了。
那这个问题怎么解决呢,很简单,./cppd -qws,加个-qws就可以,这个是什么意思,这个就是说告诉qt,这个应用程序同时会作为server,所以会开启一个server,其源码实现是在
QApplication a( argc, argv );
构建qapplication是创建的,当construct发现参数有qws时就会开启一个server进程运行qwserver类的实例,并初始化。
让我们来看下代码实现,由于argv只传给了qapplication,应该可以看出在qapplication的构造函数里对argv进行了识别,事实上确实如此。
QApplication::QApplication( int &argc, char **argv )
{
construct( argc, argv, GuiClient );
}
void QApplication::construct( int &argc, char **argv, Type type )
{
qt_appType = type;
qt_is_gui_used = (type != Tty);
init_precmdline();
static char *empty = (char*)"";
if ( argc == 0 || argv == 0 ) {
argc = 0;
argv = ∅
}
qt_init( &argc, argv, type ); // Must be called before initialize()
process_cmdline( &argc, argv );
#if defined(QT_THREAD_SUPPORT)
qt_mutex = new QMutex(TRUE);
#endif
initialize( argc, argv );
}
void qt_init( int *argcptr, char **argv, QApplication::Type type ) {
...................
else if ( arg == "-qws" ) {
type = Qapplication::GuiServer;
...................
if ( type == QApplication::GuiServer ) {
qt_appType = type;
qws_single_process = TRUE;
QWSServer::startup(flags);//这个就会创建server
setenv("QWS_DISPLAY", qws_display_spec, 0);
}
}
QWSServer::startup(flags);//创建的server,qwsServer会指向它。
extern QWSServer *qwsServer; //there can be only one
现在让我们来看下。当然由于我们要使用qvfb仿真framebuffer,当然就要开启qvfb
这个时候就看到。
还有一点要注意的是:qt4不再有setmainwidget函数了。
下面是交叉版本的编译:
其实交叉编译qte是差不多的,只是
./configure参数不一样:
./configure -prefix /home/jimmy/opt/run/qt-arm -release -shared -fast -pch -no-qt3support -qt-sql-sqlite -no-libtiff -no-libmng -qt-libjpeg -qt-zlib -qt-libpng -qt-freetype -no-openssl -nomake examples -nomake demos -nomake tools -optimized-qmake -no-phonon -no-nis -no-opengl -no-cups -no-xcursor -no-xfixes -no-xrandr -no-xrender -no-xkb -no-sm -no-xinerama -no-xshape -no-separate-debug-info -xplatform qws/linux-arm-g++ -embedded arm -depths 16 -no-qvfb -qt-gfx-linuxfb -no-gfx-qvfb -no-kbd-qvfb -no-mouse-qvfb -qt-kbd-usb -confirm-license
后面一大堆参数,其实就是为了尽量减少库的大小,而剪裁一些不需要的功能。
然后make
make install
然后就是应用程序的交叉编译。
这个时候要首先设置环境变量,这样
1 export QTDIR=$HOME/opt/run/qt-arm
2 export QTEDIR=$QTDIR
3 export PATH=$QTDIR/bin:$QPEDIR/bin:$PATH
4 export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
然后到新建一个文件,main.cpp
qmake -project
qmake
make
即了
其实在第二步隐藏了一个细节,qmake 等价于qmake -spec default,这个spec参数
就是到mkspecs文件夹找配置文件。
jimmy@jimmy-linux:~/opt/run/qt-arm$ ls
bin include lib mkspecs plugins setenv.sh tmake translations
jimmy@jimmy-linux:~/opt/run/qt-arm$ cd mkspecs
jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs$ ls -l
总用量 336
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-g++
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-g++-64
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-xlc
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 aix-xlc-64
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 common
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 cygwin-g++
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 darwin-g++
lrwxrwxrwx 1 jimmy jimmy 17 2009-05-05 15:28 default -> qws/linux-arm-g++
drwxr-xr-x 5 jimmy jimmy 4096 2009-05-05 15:28 features
drwxr-xr-x 2 jimmy jimmy 4096 2009-05-05 15:28 freebsd-g++
也就是说default其实是使用linux-arm-g++
jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs$ cd qws/linux-arm-g++/
jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs/qws/linux-arm-g++$ ls
qmake.conf qplatformdefs.h
jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs/qws/linux-arm-g++$ cat qmake.conf
#
# qmake configuration for linux-g++ using the arm-linux-g++ crosscompiler
#
TEMPLATE = app
CONFIG += qt warn_on release link_prl
QT += core gui network
QMAKE_INCREMENTAL_STYLE = sublib
QMAKE_CC = arm-linux-gcc
QMAKE_LEX = flex
QMAKE_LEXFLAGS =
QMAKE_YACC = yacc
QMAKE_YACCFLAGS = -d
QMAKE_CFLAGS = -pipe
QMAKE_CFLAGS_WARN_ON = -Wall -W
QMAKE_CFLAGS_WARN_OFF =
QMAKE_CFLAGS_RELEASE = -O2
QMAKE_CFLAGS_DEBUG = -g
QMAKE_CFLAGS_SHLIB = -fPIC
QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses
QMAKE_CFLAGS_THREAD = -D_REENTRANT
QMAKE_CFLAGS_HIDESYMS = -fvisibility=hidden
QMAKE_CXX = arm-linux-g++
QMAKE_CXXFLAGS = $$QMAKE_CFLAGS
QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON
QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE
QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG
QMAKE_CXXFLAGS_SHLIB = $$QMAKE_CFLAGS_SHLIB
QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC
QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD
QMAKE_CXXFLAGS_HIDESYMS = $$QMAKE_CFLAGS_HIDESYMS -fvisibility-inlines-hidden
QMAKE_INCDIR =
QMAKE_LIBDIR =
QMAKE_INCDIR_X11 =
QMAKE_LIBDIR_X11 =
QMAKE_INCDIR_QT = $$[QT_INSTALL_HEADERS]
QMAKE_LIBDIR_QT = $$[QT_INSTALL_LIBS]
QMAKE_INCDIR_OPENGL =
QMAKE_LIBDIR_OPENGL =
QMAKE_INCDIR_QTOPIA = $(QPEDIR)/include
QMAKE_LIBDIR_QTOPIA = $(QPEDIR)/lib
QMAKE_LINK = arm-linux-g++
QMAKE_LINK_SHLIB = arm-linux-g++
QMAKE_LFLAGS =
QMAKE_LFLAGS_RELEASE =
QMAKE_LFLAGS_DEBUG =
QMAKE_LFLAGS_SHLIB = -shared
QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB
QMAKE_LFLAGS_SONAME = -Wl,-soname,
QMAKE_LFLAGS_THREAD =
QMAKE_RPATH = -Wl,-rpath,
QMAKE_LIBS =
QMAKE_LIBS_DYNLOAD = -ldl
QMAKE_LIBS_X11 =
QMAKE_LIBS_X11SM =
QMAKE_LIBS_QT = -lqte
QMAKE_LIBS_QT_THREAD = -lqte-mt
QMAKE_LIBS_QT_OPENGL = -lqgl
QMAKE_LIBS_QTOPIA = -lqpe -lqtopia
QMAKE_LIBS_THREAD = -lpthread
QMAKE_LIBS_OPENGL =
QMAKE_MOC = $$[QT_INSTALL_BINS]/moc
QMAKE_UIC = $$[QT_INSTALL_BINS]/uic
QMAKE_AR = arm-linux-ar cqs
QMAKE_OBJCOPY = arm-linux-objcopy
QMAKE_RANLIB = arm-linux-ranlib
QMAKE_TAR = tar -cf
QMAKE_GZIP = gzip
QMAKE_COPY = cp -f
QMAKE_MOVE = mv -f
QMAKE_DEL_FILE = rm -f
QMAKE_DEL_DIR = rmdir
QMAKE_STRIP = arm-linux-strip
QMAKE_CHK_DIR_EXISTS = test -d
QMAKE_MKDIR = mkdir -p
load(qt_config)
jimmy@jimmy-linux:~/opt/run/qt-arm/mkspecs/qws/linux-arm-g++$
这样qmake就可利用这些信息生成交叉编译的makefile,
然后我们执行make即可生成针对arm的QT应用程序。
下面来看下qtopia的原理:
其实qtopia就是一个桌面qt应用程序,但是他扩展了一些类,所以,他也有库,头文件,
然后生成的qpe就是利用这些扩展的库实现的。
我们来看下qtopia的原理;
既然它也是应用程序,那它就有main函数,找到这个函数
int main( int argc, char ** argv )
{
int retVal = initApplication( argc, argv );
if ( DesktopApplication::doRestart ) {
qDebug("Trying to restart");
execl( (QPEApplication::qpeDir()+"bin\\qpe").latin1(), "qpe", 0 );
}
return retVal;
}
很简单,其实就一个 initApplication函数
int initApplication( int argc, char ** argv )
{
cleanup();
#ifdef QT_QWS_CASSIOPEIA
initCassiopeia();
#endif
#ifdef QPE_OWNAPM
initAPM();
#endif
#ifdef QT_DEMO_SINGLE_FLOPPY
initFloppy();
#endif
initEnvironment();
//Don't flicker at startup:
#ifdef QWS
QWSServer::setDesktopBackground( QImage() );
#endif
ServerApplication a( argc, argv, QApplication::GuiServer );//这个就启动一个qwserver类,也就有了服务器了
refreshTimeZoneConfig();
initBacklight();
initKeyboard();
// Don't use first use under Windows
#ifdef Q_OS_UNIX
if ( firstUse() ) {
a.restart();
return 0;
}
#endif
AlarmServer::initialize();
#if defined(QT_QWS_LOGIN)
for( int i=0; i
if( strcmp( a.argv()[i], "-login" ) == 0 ) { // No tr
QDMDialogImpl::login( );
return 0;
}
#endif
Server *s = new Server();//这个其实就是创建主窗口,桌面窗口,这个类是widget继承的
(void)new SysFileMonitor(s);
#ifdef QWS
Network::createServer(s);
#endif
s->show();
int rv = a.exec();
qDebug("exiting...");
delete s;
return rv;
}
QWSServer::setDesktopBackground( QImage() ); 这个是静态函数,生成了一个image对象, 和qwsserver(指针,指向qserver对象)一样,其他文件以extern qwsserver来使用这些对象,保证了唯一性。
网上有很多文章是:
在./configure前就指定了
export QTDIR=$PWD/qt
26 export QPEDIR=$PWD/qtopia
29 export PATH=$QTDIR/bin:$QPEDIR/bin:$PATH
这让人产生错觉,好像./configure,make过程和上面的环境变量有关,其实没有关系,因为这个用于生成Lib,所以不需要指定和qt有关的库,所以qtdir没有用,
INCPATH = -I../../mkspecs/linux-g++ -
LINK = g++
且头文件文件夹都是用相对路径,所以就不需要这些变量。当然QT4以前还是要这些环境变量的否则会提示没有设置qtdir等环境变量。
在应用程序的时候就不一样,因为这个时候是要使用这些变量,且应用程序和头文件夹的位置没有太大的关系,这样就只有用环境变量来指定,这个其实在qmake时,qmake就会使用这些变量来生成makefile
下面来说下使用designer编写qt程序。
首先通过designer会产生一个.ui结尾的文件,
假设是hello.ui
然后
uic -o test.h hello.ui
然后就会生产对应的类。
然后我们编写代码包含这个头文件,就相当于使用了这个类。
jimmy@jimmy-linux:~/qt/first$ cat hello.cpp
#include
#include
#include "test.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QDialog *window = new QDialog;
Ui::Dialog *form = new Ui::Dialog;
form->setupUi(window);
window->show();
return app.exec();
}
jimmy@jimmy-linux:~/qt/first$
Ui::Dialog;就是用designer生成由uic处理后生成的类,
然后:
qmake -project
qmake
make就可以了
如果上面的qmake命令执行失败,那就是你的环境变量没有设置好。
然后运行