最近在学习使用Qt4进行GUI的开发。经过几天的学习,基本上清楚了大致的开发流程。本文以一个简单的文本编辑器为例,说明Qt4的基本开发流程。
之所以选用文本编辑器为例是因为它属于一个标准的主窗口,整个用户界面包含菜单、工具栏、状态栏以及应用程序所需要的对话框。效果图如下:
类似这样的主窗口实现需要三个步骤:用户界面的设计、建立信号与槽之间的连接和自定义槽的实现。Qt4为开发人员提供了两种实现用户界面的方法,一种是通过传统的编码方式实现用户界面;另一种是通过Qt designer(现被集成到Qt Creator中)快速实现。这两种实现方式只是体现在用户界面的设计上,虽然在后续的两个步骤中稍有不同,但本质上是相同的。为了方便说明,本文将这两种设计方式分开描述。
1. 使用编码方式实现
此部分所描述的文本编辑器对应的工程文件为code_myTextEdit,该文本编辑器对应的类为MainWindow,它继承了QMainWindow类。该工程内部所有的文件构成如下图:
1.1 用户界面的实现
如果使用编码方式,则需要在定义MainWindow类的时候声明用户界面所需要的所有部件。比如该文本编辑器的有三个菜单,则需要分别声明三个QMenu类型的变量:menu_File、menu_Edit和menu_Help。此外,还需要声明一些QAction类型的变量,该类型的变量代表鼠标点击菜单或工具栏上相应选项时所产生的动作。比如File菜单中的New选项就对应QAction类型的变量newAct。除上述的成员变量外,在类的定义中还必须声明所需的槽函数。以上这些声明通常被单独放在一个头文件中,即mainwindow.h。
声明完毕后,对用户界面的建立常常会被安排在对应类的构造函数中。其实现通常在.cpp文件中完成,比如mainwindow.cpp。比如在MainWindow类的构造函数中就有createMenus()、createActions()、createToolBars()和createStatusBar()几个函数,他们分别创建主窗口的菜单、菜单选项、工具栏和状态栏。
1.2 建立信号和槽之间的连接
第一部分的代码实现了用户界面,接下来应该建立信号和槽之间的连接。Qt中的信号和槽机制与Java中的事件驱动机制类似,这里的信号相当于用户所引发的事件,而槽本质上就是一个函数,它实现了这个动作所对应的功能。
Qt中使用connect函数来建立信号和槽之间的链接。通常一个信号可以链接多个槽,多个信号也可以链接同一个槽,并且一个信号还可以与另一个信号相连接。连接函数通常放在相关类的构造函数中完成,上述的文本编辑器所对应的MainWindow类的构造函数中,createActions()不仅创建了相应事件的动作对象,而且完成了所有可能引发的事件与相应槽之间的连接。下面是New菜单选项的创建代码:
newAct = new QAction(tr("&New"), this); newAct->setShortcuts(QKeySequence::New); newAct->setStatusTip(tr("Create a new file")); connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
connect函数说明当用户对New选项发出“触发”动作时,当前的对象就调用newFile函数进行事件处理。这里的触发即包含鼠标点击又包含快捷键等。
1.3 实现自定义的槽函数
这部分应该是最关键的一步,根据每个事件的具体功能实现相应的槽函数。比如下面的代码就是实现New菜单选项:
void MainWindow::newFile() { if (maybeSave()) { textEdit->clear(); setCurrentFile(""); } }
其中maybeSave()用来判断当前已修改的文件是否进行了保存;如果已保存则清空当前的文本编辑器,并设置相应的主窗口的标题名。其中,maybeSave()和setCurrentFile()都需要用户自己实现。
将第一步声明的所有槽函数都实现后,文本编辑器也基本上算开发完成。
2. 使用设计方式实现
2.1 用户界面的实现
相比传统的编码方式,使用Qt designer则可以快速设计出所需要的用户界面。通过从工具栏中拖动相应的部件到主窗口就可以完成用户界面的涉及。关于主窗口的设计可以参考Qt在线手册中的Creating Main Windows in Qt Designer一节,这里不再赘述。此部分所说的文本编辑器对应的工程文件为design_myTextEdit,内部文件如下图:
如果使用这种可视化的设计方式,则会使用.ui文件保存这个用户界面。用户界面编译器(user interface compiler,uic)则会将.ui文件转换成适合C++的.h文件。比如上述的文本编辑器的用户界面存储在mainwindow.ui中,uic会将其转换成ui_mainwindow.h文件。所生成的ui_mainwindow.h文件中包含了类Ui::MainWindow的定义,该类是一个与mainwindow.ui等价的C++文件。这个类中声明了用户界面中的一些部件以及初始化窗体的setupUi()。
具体在使用时,则通常再定义一个新的类MainWindow,让这个类继承QMainWindow和Ui::MainWindow两个类。此时,就不需要MainWindow中声明窗体中的各个部件了,因为这已经在mainwindow.ui文件中完成;同时对窗体的初始化也可以通过调用setupUi(this)自动完成。这一点是与用编码方式实现用户界面所不同的。
2.2 建立信号和槽之间的连接
使用设计方式创建完主窗口后,建立信号和槽之间的连接也十分简单。只需在事件编辑器中选取具体事件和具体的触发动作,并点击右键的go to slot选项即可在mainwindow.h和mainwindow.cpp文件中创建相应槽函数声明。
2.3 实现自定义的槽函数
到此步只需在实现相应的槽函数即可。比如上述save选项对应的槽如下所示,现在只需输入相应的实现代码即可。
void MainWindow::on_action_Save_triggered() { save(); }
因此,从上述内容来看,创建用户界面的这两种方式在本质上是相同的。虽然设计方式简单方便,但是对于初学者最好两种方法都掌握,这样才能理解用户界面的开发方式。
参考:
1. Qt Assistant
2. http://www.yafeilinux.com/?page_id=3
3. C++ GUI Qt 4编程(第二版); 电子工业出版社;Blanchette,J,Summerfield,M 著;闫锋欣 等译;