第8章主窗口
本章重点
★Qt中主窗口程序的框架
★手工创建QMainWindow主窗口的基本步骤
★结合Qt Designer创建主窗口的方法
★QAction动作的创建
★如何在菜单及工具栏中加入动作
★如何加入锚接窗口
★各种锚接方式以及锚接窗体特性的设置方法
★创建多文档应用程序的方法和步骤
在前面几章中,我们一起学习了Qt的基础知识和技能。这一章,我们将带领大家学习如何创建主窗口应用
程序,包括菜单栏、工具栏、状态栏、动作、中心部件、锚接部件等的创建和使用。最后,将介绍创建多文档窗口的常用方法和步骤。
8.1主窗口框架
Qt的QMainWindow类提供了一个应用程序主窗口,包括一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)以及一个中心部件(central widget),常见的一种界面布局如图8-1所示。
图8-1Qt主窗口常见布局示意图
绝大多数现代GUI应用程序都会提供一些菜单、上下文菜单和工具栏。
Qt通过引入“动作”(action)这一概念来简化有关菜单和工具栏的编程。一个动作就是一个可以添加到任意数量的菜单和工具栏上的项。
1.菜单栏
菜单是一系列命令的列表。菜单可以让用户浏览应用程序并且处理一些事务,上下文菜单和工具栏则提供了对那些经常使用的功能进行快速访问的方法,它们能够提高软件的使用效率。
为了实现菜单、工具栏按钮、键盘快捷方式等命令的一致性,Qt使用动作(Action)来表示这些命令。Qt的菜单就是由一系列的QAction动作对象构成的列表。而菜单栏则是包容菜单的容器,它通常位于主窗口的顶部,标题栏的下面。一个主窗口通常只有一个菜单栏。
2.工具栏
工具栏是由一系列的类似于按钮的动作排列而成的面板,它通常由一些经常使用的命
令(动作)组成。工具栏的位置处在菜单栏下面、状态栏的上面,工具栏可以停靠在主窗口的上、下、左、右这4个不同的位置。一个主窗口可以有多个工具栏。
3.状态栏
状态栏通常是显示GUI应用程序的一些状态信息,它位于主窗口的最底部。可以在状态栏上添加、使用Qt窗口部件。一个主窗口只有一个状态栏。
4.锚接部件
对于一个标准的Qt主窗口而言,锚接部件不是必需的。锚接部件一般是作为一个容器来使用,以包容其他窗口部件来实现某些功能。比如Qt设计器的属性编辑器、对象监视器等都是由锚接部件包容其他的Qt窗口部件来实现的。它处在工具栏的内部,可以作为一个窗口自由的浮动在主窗口的上面,也可以像工具栏一样停靠在主窗口的左、右、上、下四个方向上。一个主窗口可以包含多个锚接部件。
5.中心窗口部件
中心窗口部件处在锚接部件的内部,它位于主窗口的中心,一个主窗口只有一个中心窗口部件。主窗口QMainWindow具有自己的布局管理器,因此在QMainWindow窗口上设置布局管理器或者创建一个父窗口部件为QMainWindow的布局管理器都是不允许的。但可以在主窗口的中心窗口部件上设置布局管理
器。
6.上下文菜单
为了控制主窗口工具栏和锚接部件的显隐,在默认情况下,QMainWindow主窗口提供了一个上下文菜单(Context Menu)。通常,通过在工具栏或锚接部件上单击鼠标右键就可以
激活该上下文菜单;也可以通过函数CMainWindow::createPopupMenu()来激活该菜单。此外,还可以重写CMainWindow::createPopupMenu()函数,实现自定义的上下文菜单。
8.2创建主窗口的方法和流程
8.2.1方法
创建应用程序主窗口界面主要有两种方法:
1.全部代码生成,单继承自QMainWindow类,在子类的实现文件中使用代码创建应用程序主窗口的菜单、工具栏、锚接部件以及状态栏等并设置它们的属性;使用单继承Qt窗口部件类的方法生成中心部件并添加到主窗口中。
2.使用Qt设计师绘制应用程序主窗口,在Qt设计师中添加菜单(以及子菜单和动作)、工具栏(以及动作)、锚接部件(以及子窗口部件)、状态栏(目前,Qt设计师没有提供状态栏的设计编辑功能,比如无法将窗口部件直接拖放到主窗口的状态栏上)等并设置它们的属性,以及关联一些基本的信号和槽;然后采用前面介绍的“单一继承方式”或“多继承方式”实现应用程序主窗口的代码。这种方法需要和手写代码方法相结合。
一般的,采用第2种方法创建应用程序主窗口是比较快的,并且具有直观易懂的优势。
8.2.2流程
无论采用哪种方法,创建主窗口应用程序一般遵循如下步骤:
⒈创建主菜单
⒉创建子菜单
⒊创建动作
⒋创建工具栏
⒌动作和菜单项以及工具栏按钮的关联
⒍创建锚接窗口(不是必需的)
⒎创建中心窗口部件
⒏创建状态栏
这其中,依据采用手写代码和使用Qt Designer的不同,上述步骤有些不是必需的,或者不是显式的。下面我们先来看看如何使用手写代码创建主窗口程序。
8.3代码创建主窗口
本实例实现一个基本的主窗口程序,包含一个菜单条、一个工具栏、中央可编辑窗体及状态栏。实现的效果如图8-2所示。
8.3.1头文件
主窗口头文件代码如下:
1#ifndef MAINWINDOW_H
2#define MAINWINDOW_H
3#include<QMainWindow>
4class QAction;
5class QMenu;
6class QToolBar;
7class QTextEdit;
8class MainWindow:public QMainWindow 9{
10Q_OBJECT
11public:
12MainWindow();
13void createMenus();
14void createActions(); 15void createToolBars(); 16void createStatusBar();
17public slots:
18void slotNewFile();
19void slotOpenFile();
20void slotSaveFile();
21void slotCopy();
22void slotCut();
23void slotPaste();
24void slotAbout();
25private:
26QTextCodec*codec;
27QMenu*menuFile;
28QMenu*menuEdit;
29QMenu*menuAbout;
30QToolBar*toolBarFile;
31QToolBar*toolBarEdit;
32QAction*actionOpenFile;
33QAction*actionNewFile;
34QAction*actionSaveFile;
35QAction*actionExit;
36QAction*actionCopy;
菜单栏包括
37QAction*actionCut;
38QAction*actionPaste;
39QAction*actionAboutQt;
40QTextEdit*text;
41};
42#endif//MAINWINDOW_H
第1和第2句定义头文件包含卫哨,目的是防止重复包含头文件,这两句与结尾的第42句结合在一起使用才是完整的。
第3句包含了QMainWindow的定义,它是主窗口类的基类。
第4至第7句对程序下文中可能用到的类进行前置声明(forward declaration)。它们会告诉编译器,我们用到的这些类已经存在了,并且不需要知道这些类的完整定义。我们为什么要这样做,而不是将它们的头文件包含进来呢?这主要是由于在程序下文中,我们只是简单的定义了指向这些类的对象的指针,而并没有涉及到该类的其他方面。
这样做的好处,一是避免了头文件被其他文件多次包含,尤其是在头文件中包含头文件时,容易造成重复包含和产生包含顺序问题,并且增大了文件的体积;二是提高了编译速度,因为编译器只需知道该类已经被定义了,而无需了解定义的细节。
小贴士:尽量不要在头文件中包含另外的头文件
一种好的编程风格是,尽量在头文件中使用类前置声明程序下文中要用到的类,实在需要包含其它的头文件时,可以把它放在我们的类实现文件中。在下面的程序中,你将会看到这个准则的应用。
第8句声明了我们的MainWindow类是派生自QMainWindow。
第10句的Q_OBJECT宏对于所有使用了信号/槽机制的类而言是必需的,同时它要求被放置在类声明的开始处。
第12句声明了我们的主窗口类MainWindow的构造函数。
在第13至第16句中,createActions()函数用于创建程序中用到的动作(Action),createMenus()函数用于创建菜单(Menu),createToolBars()函数用于创建工具栏(ToolBar),CreateStatusBar()函数用于创建状态栏(StatusBar)。接着声明了用到的