精通Qt4编程
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

初级篇

第1章 Qt初步实践

作为Qt程序开发之旅的第一站,本章将阐述如何使用Qt开发一个简单的GUI用户界面程序。在这一章,将学习如何建立Qt主程序、建立qmake工程,还将接触到“信号(signal)”和“槽(slot)”以及“Qt布局”等基本概念。随着学习的不断深入,将在第3章和第5章对这些概念进行深入的讲解,并演示它们在GUI用户界面设计中的应用。

1.1 第一个Qt程序

在这一节,学习创建第一个较简单的Qt应用程序。在这个程序中,用户界面将显示一行中文“同一个世界,同一个梦想!”通过这个程序,将学会使用两种手段建立Qt应用程序:KDevelop集成开发环境和vim编辑器。

1.1.1 建立主程序

首先,看一下第一个Qt GUI应用程序hello的源代码,其内容如下所示(为了便于读者查阅相关源代码,代码的第一行注释了该文件在源代码中的路径)。

        // chapter01/hello/src/hello.cpp.
        #include <QtGui/QApplication>
        #include <QtGui/QWidget>
        #include <QtGui/QLabel>
        #include <QtCore/QTextCodec>
        int main(int argc, char* argv[])
        {
            QApplication app(argc, argv);
            QTextCodec::setCodecForTr(QTextCodec::codecForName("gb18030"));
            QWidget* pWidget = new QWidget;
            QLabel label(pWidget);
            label.setText(QObject::tr("同一个世界,同一个梦想!"));
            pWidget->show();
            return app.exec();
        }

这个程序的功能是在一个窗口中显示“同一个世界,同一个梦想!”,运行效果如图1-1所示。

图1-1 第一个Qt程序

注意

窗口(Window)和窗口部件(Widget)

本书中,多次使用了窗口和窗口部件的概念。把一个图形用户界面称为窗口,它往往具有标题栏、窗口边框(frame)、能够通过鼠标拖动和改变大小等特性,最典型的窗口就是对话框。例如,第一个Qt应用程序的用户界面就是一个窗口。当文中使用窗口的时候,就是特指这种情况。

一般的,窗口部件是对所有图形用户界面的统称,它既可以作为单独的窗口出现,也可以出现在一个窗口的内部。

1.1.2 建立工程

现在暂不分析第一个Qt应用程序是如何运行的,而是先为它建立qmake工程,然后进行调试和运行。

在Linux系统中,可以有多种方法输入、编辑上述Qt程序,此处将使用vim文本编辑器和KDevelop工具建立上述主程序。

1.在vim中建立Qt应用程序

在vim中建立Qt应用程序,步骤如下(如果读者觉得在vim文本编辑器中编辑、调试以及运行Qt应用程序比较麻烦,可以直接跳过这一部分,而选择在KDevelop集成开发环境中建立该Qt应用程序):

打开控制台程序konsole,将当前目录切换到相应的路径下,执行控制台命令“mkdir hello”建立hello目录,执行 “cd hello”命令进入该目录。

在控制台执行“vim hello.cpp”命令(如果文件hello.cpp已经存在,则打开文件;否则新建hello.cpp文件),打开vim文本编辑器。

进入vim编辑器后按“i”键(即打开vim编辑器的修改编辑功能),然后输入第一个Qt应用程序hello的源代码。

按Esc键,退出vim文本编辑器的编辑功能。

最后,在vim文本编辑器的命令行输入命令“:wq”,按回车键后vim将保存文件并退出。

现在,第一个Qt应用程序hello已经输入到vim文本编辑器中了。关于vim编辑器的使用读者可查阅相关帮助文档,在此不再赘述。

2.在KDevelop中建立Qt应用程序

KDevelop是集编辑、编译、调试和运行C++程序等诸工具于一身的应用程序集成开发环境。在KDevelop中建立Qt应用程序并进行编译、调试、运行都是比较简单的,但需要建立KDevelop工程(这和使用VC++进行应用程序开发是类似的),对于初学者来说过程有些复杂。具体步骤如下:

打开KDevelop后,选择菜单“工程”|“新建工程”,如图1-2所示。

在“建立新工程”对话框的“所有工程”选项卡中,选择“C++ | QMake project | Basic Qt4 Application”,选择或者输入存放位置(例如,“/home/lcf/book/chapter01”),输入应用程序名称“hello”(KDevelop将会在/home/lcf/book/chapter01/路径下建立hello目录),单击“下一步”按钮,如图1-3所示。

设置“工程选项”,在此输入Qt4的qmake和Qt设计器的绝对路径,直接单击“下一步”按钮,如图1-4所示。

设置“版本控制系统”,略过,单击“下一步”按钮,如图1-5所示。

图1-2 新建KDevelop工程

图1-3 建立hello工程

图1-4 设置工程选项

图1-5 设置版本控制

在“h文件的模板”选项(如图1-6所示)中,可以设置头文件 .h的格式(在此省略);单击“下一步”按钮进入“cpp文件的模板”选项卡(如图1-7所示),与“h文件的模板”类似。

最后,单击“完成”按钮,KDevelop会自动生成一个标准的C++主程序。在此,编辑修改为第一个Qt应用程序hello的源代码,如图1-8所示。

图1-6 设置头文件模板

图1-7 设置实现文件模板

图1-8 第一个KDevelop工程

到此,在KDevelop中已经建立了一个KDevelop工程,并且输入了第一个Qt应用程序hello。接下来,建立Qt应用程序的qmake工程文件。

有两种方法建立qmake工程:自动生成和手动建立。下面分别描述如何使用这两种方法建立应用程序hello的qmake工程。

3.自动建立qmake工程

对于比较简单的小应用程序,使用qmake命令自动建立的qmake工程完全可以满足需要。

前面,已经在vim文本编辑器中输入了第一个Qt应用程序hello的源代码,现在为它建立相应的qmake工程。

首先,在控制台konsole中将当前目录切换到hello.cpp文件所在的目录,运行“qmake –project”命令。此时Qt的qmake工具将在当前目录下自动生成应用程序hello的工程文件hello.pro,其内容如下。

        TEMPLATE = app
        TARGET =
        DEPENDPATH += .
        INCLUDEPATH += .
        # Input
        SOURCES += hello.cpp

下面分析一下这个qmake工程文件。

前3行文本是qmake工具自动添加的注释文本,它描述了该qmake工程文件是由qmake工具自动生成的,以及文件生成的时间和qmake工具的版本号。

变量TEMPLATE描述了为建立目标文件而采用何种模板,即生成何种形式的Makefile文件。qmake工具定义了5种模板:

● 应用程序app,为建立一个Qt应用程序创建Makefile文件;

● 库lib,为建立应用程序库而创建Makefile文件;

● 子工程subdirs,为建立子目录下的目标文件创建一个Makefile文件,子目录通过变量SUBDIRS指定(子目录下的工程文件也需要指出使用何种模板);

● VC应用程序vcapp,为Visual Studio生成一个应用程序工程,仅仅用于Windows操作系统。

● VC库vclib,为Visual Studio生成一个应用程序库工程,仅仅用于Windows操作系统。

由于第一个Qt程序是一个可直接执行的应用程序,因此采用“应用程序app”模板。

变量TARGET描述了目标文件的名称,即生成的应用程序的名字。qmake工具自动生成的qmake工程文件采用默认方式(TARGET的值为空),即应用程序的名字采用工程文件hello.pro所在的文件夹的名字hello。

变量DEPENDPATH描述了建立应用程序所依赖的其他文件所在的路径。hello.pro工程文件中将该值设置为当前目录。

变量INCLUDEPATH描述了编译该工程时编译器需要搜索的#include路径。hello.pro工程文件中将该值设置为当前目录。

变量SOURCES选项告诉编译器,源代码文件的路径(相对于工程文件hello.pro的位置)及其文件名字。应用程序hello比较简单,只包含一个hello.cpp源文件。

现在,在KDevelop中建立qmake工程文件。

事实上,在KDevelop中建立KDevelop工程的时候,KDevelop会在约定的目录下建立了一个与应用程序同名的hello目录,它的主要目录结构如图1-9所示。

其中,

● bin目录存放可执行的目标文件;

● src目录存放源文件(包括qmake工程文件src.pro以及资源文件等);

● templates目录存放KDevelop生成源文件.cpp和头文件.h时使用的模板;

● hello目录下的其他文件都是关于KDevelop工程的(hello.pro是自动生成的qmake工程文件)。

图1-9 第一个Qt程序的目录结构

可以看到,KDevelop在上面的目录结构中生成了两个qmake工程文件:hello目录下的hello.pro主工程文件和hello/src目录下的src.pro子工程文件。

主工程文件hello.pro的内容如下。

        TEMPLATE=subdirs
        SUBDIRS=src

第1行的TEMPLATE变量的值为subdirs,表示hello.pro工程在子文件夹中还包含子工程。子工程文件所在的目录由第2行SUBDIRS变量指定。在运行qmake生成Makefile文件的时候,qmake工具会根据主工程hello.pro中的SUBDIRS选项自动到相应的目录下寻找src.pro子工程文件,qmake工具将联合两个工程文件分别生成两个前后相关的Makefile文件。

qmake子工程文件src.pro的内容如下。

        SOURCES=hello.cpp
        TARGET=../bin/hello

该工程文件中,大多数变量(或选项)采用了默认值,仅仅定义了SOURCES和TARGET工程选项,在此不再详述。

注意

工程文件中的源文件.cpp和头文件.h的位置,既可以采用绝对路径表示,也可以采用相对路径表示。相对路径意味着源文件或头文件相对于工程文件.pro所在目录的路径。例如,在第一个Qt应用程序的例子中,src.pro工程文件在./hello/src目录下,hello.cpp源文件也在./hello/src目录下。那么采用相对路径时,工程文件src.pro的选项SOURCES的值为:

            ./hello.cpp(或hello.cp)

而采用绝对路径时,则值为:

            < KDevelop工程所在的绝对路径>/src/hello.cpp

其他工程选项也是类似的,比如src.pro工程文件中的TARGET变量值为../bin/hello,表示目标文件将被放置在< KDevelop工程所在的绝对路径> /bin目录下面。

4.手动建立qmake工程

建立较大型Qt应用程序时,使用qmake工具生成的qmake工程文件将无法满足编程的需要。尽管qmake工具生成的工程文件完全可以满足hello程序的编译需求,但作为一个例子,笔者生成了一个对应于vim输入的源代码文件的较简单的hello.pro文件,它位于hello目录下。其内容如下:

        TEMPLATE = app
        TARGET = hello
        DESTDIR = .
        CONFIG  +=  debug \
                warn_on
        OBJECTS_DIR=    ./tmp
        SOURCES +=  hello.cpp

下面,简单地分析一下这个工程文件。

● 变量TARGET定义了可执行目标文件的名字为hello。

● 变量DESTDIR定义了存放可执行目标文件hello的路径,即在hello目录下。

● 变量CONFIG定义了编译选项,即:

debug表示建立的目标代码是调试版本(相对于release发布版本);

warn_on要求编译器在编译应用程序时打开警告开关。

● 变量OBJECTS_DIR描述了编译/连接应用程序过程中产生的中间文件存放的位置,即将编译器生成的中间文件hello.o放置在工程文件所在目录的tmp子目录下(hello/tmp)。

在为应用程序hello建立qmake工程后,就可以编译运行第一个Qt应用程序了。

1.1.3 编译/运行第一个Qt应用程序

下面,通过两种方式编译、运行第一个Qt应用程序。

在控制台模式下,依次在工程文件所在的目录下执行下述命令:

● qmake,生成Makefile文件;

● make,编译、连接并生成可执行代码;

● ./hello,执行应用程序。

在KDevelop中,编译、运行Qt应用程序的步骤如下:

在右侧“QMake管理器”选项卡的“src”子工程下单击右键,选择菜单命令“运行qmake”,建立Makefile文件,如图1-10所示。

选择“编译”菜单中的“编译工程”命令(或单击工具栏中的“编辑工程”工具按钮),编译、连接Qt程序,如图1-11所示。

选择“编译”菜单中的“执行主程序”命令(或单击工具栏中的“执行主程序”工具按钮),运行hello程序。应用程序的运行效果如图1-12所示。

图1-10 KDevelop中运行qmake

图1-11 执行编辑工程命令

图1-12 第一个Qt程序运行效果

1.1.4 第一个Qt程序的代码分析

现在再回过头来逐行地分析一下源代码,看看第一个Qt应用程序是如何运行的。

这是一个非常简单的Qt应用程序,它不包含任何自定义的类,且仅仅有一个hello.cpp主程序文件。

        // chapter01/hello/src/hello.cpp.
        #include <QtGui/QApplication>
        #include <QtGui/QWidget>
        #include <QtGui/QLabel>
        #include <QtCore/QTextCodec>

首先包含必要的Qt类的头文件。

● #include <QtGui/QApplication> 表示包含Qt的应用程序类QApplication的头文件,其中的QtGui表示Qt的QtGui模块,从目录结构来讲,QtGui是文件夹。<QApplication>是Qt定义QApplication类的头文件(与类名相同);QApplication类是每一个Qt GUI应用程序所必需的。

● #include <QtGui/QWidget> 将Qt的基础窗口部件QWidget的定义包含进来;接下来的#include<QtGui/QLabel> 将Qt的标签QLabel头文件包含进来。事实上,QLabel头文件中已经包含了QWidget的定义,因此也可以去掉对QWidget头文件的包含。

● #include <QtCore/QTextCodec> 用于包含QtCore模块下的QTextCodec类的头文件。类QtextCodec封装定义了显示文本(例如,“同一个世界,同一个梦想”)字符集的转化功能。

扩展阅读

Qt4定义了多个模块,每个模块包含相对独立的库文件并实现各自相应的功能。Qt4的软件开发模块有:

● QtCore,Qt4的基本模块,定义了其他模块使用的Qt核心的非GUI类,所有其他的模块都依赖于该模块。

● QtGui,定义了图形用户界面类。

● QtMultimedia,提供了对低级多媒体编程的支持。

● QtNetwork,定义了Qt的网络编程类。

● QtOpenGL,定义了OpenGL的支持类。

● QtOpenVG,定义了对OpenVG的支持类。

● QtScript,该模块提供了对脚本的支持。

● QtScriptTools,脚本调试器。

● QtSql,定义了访问数据库的类。

● QtSvg,定义了显示和生成SVG(Scalable Vector Graphics)类。

● QtWebkit,定义了显示和编辑Web内容的类型。

● QtXml,定义了处理XML(eXtensible Markup Language)语言的类。

● QtXmlPatters,为XML和自定义数据模型提供了XQuery和XPath引擎。

● QtDeclarative,声明式UI引擎。

● Phono,多媒体框架。

● Qt3Support,定义了同Qt4以前版本Qt3兼容的类,以使得Qt3的程序能够更容易地移植到Qt4。

还有一些模块属于Qt工具,具体有:

● QtDesigner,定义了扩展Qt设计器(Qt Designer)的类,该模块使得程序员能够为Qt设计器创建自定义的Qt窗口部件插件(widget plugins),以及创建能够访问Qt设计器组件的类;

● QtUiTools,定义了在应用程序中直接处理ui(User Interface)文件的类,它使得应用程序能够在运行时使用ui文件构建用户界面。

● QtHelp,为应用程序提供了加载Qt助手(Qt Assistant),以支持在线帮助(online help)的功能。

● QtTest,定义了对Qt应用程序和库进行单元测试(unit testing)的类。

UNIX平台的Qt4版本还包含QtDBus扩展模块,该模块提供了使用D-Bus进行进程间通信(Inter-Process Communication,IPC)的Qt类。

此外,Windows平台的Qt版本还包含两个扩展模块:

(1)QAxContainer,定义了访问ActiveX控件和COM(Component Object Model)对象的扩展;

(2)QAxServer,一个静态库,用于将一个标准的Qt二进制代码转化为COM服务器(COM server)。

在qmake工程中,默认情况下已经包含了QtCore和QtGui模块(如果不想使用QtGui模块,而仅仅使用QtCore连接程序,可以在qmake工程文件中通过使用“QT -= gui”来取消对QtGui模块的包含),因此无需配置就可以使用这两个模块中的类。而对于Qt的其他模块,在使用之前必须在qmake工程文件中通过QT选项进行配置(将在各个章节中详细阐述)。

一般可以在应用程序中通过 #include <QtGui/QtGui>包含整个QtGui模块所有类的头文件,其中第一个QtGui是模块名,第二个QtGui是QtGui模块(文件夹)下的预定义头文件(或者使用#include <QtGui>,其效果相同,不过此时<QtGui>是QtGui模块(文件夹)下的预定义头文件);也可以单独包含某个类的头文件:#include <QtGui/QApplication>(或者 #include <QApplication>)。

        int main ( int argc, char* argv[] )
        {
            QApplication app( argc, argv );

创建一个QApplication对象并将用户在控制台输入的参数传递给该应用程序对象。QApplication对象管理Qt GUI应用程序的控制流程和主要的设置选项。使用Qt设计的任何GUI应用程序,都必须包含一个QApplication对象。而对于非GUI的Qt应用程序,可以使用不依赖于QtGui库的QCoreApplication。

        QTextCodec::setCodecForTr( QTextCodec::codecForName("gb18030"));

该行代码设置QObject::tr()使用的字符集。在第一个Qt应用程序中,显示的是中文字符“同一个世界,同一个梦想!”,因此程序采用的是字符集“GB18030”(GB18030-2000是取代GBK1.0(1995年)的正式国家标准,该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030)。如果不采用正确的字符集,Qt应用程序中的中文字符将显示为乱码。如果仅仅显示英文字符的话,可以不使用该行代码,并且将下面的设置标签文本的代码修改为label.setText(“one world, one dream.”)。

        QWidget* pWidget = new QWidget;

创建一个QWidget对象。

        QLabel label( pWidget );
        label.setText( QObject::tr( "同一个世界,同一个梦想!" ) );

创建一个QLabel对象,并将该标签的父窗口部件设置为pWidget,这将使得QLabel对象放置在QWidget界面上。QLabel::setText()函数设置QLable对象的显示文本为“同一个世界,同一个梦想!”。

            pWidget->show();
            return app.exec();
        }

函数QWidget::show()将创建的图形用户界面呈现在显示器上。

最后程序返回Qt应用程序对象app执行的结果,并退出。

QApplication::exec()语句的执行,将使得Qt GUI进入一个主事件循环,直到程序中调用exit()、quit()或关闭应用程序的主窗口。主事件循环开始后,它将会接收用户界面事件以及其他事件源的事件,并向相应的窗口进行分发和处理。此外,它还完成Qt应用程序的初始化和应用程序运行结束后的善后处理,并提供会话管理(session management)。

注意

在Linux平台下,编译应用程序时,编译器经常会给出“no newline at end of file.”的警告。它的意思是说,在应用程序代码文件的最后没有新行。为了消除这个编译器告警,必须在应用程序代码的最后加一行没有任何字符(也不能包含任何的空格字符)的空行。