1.1 GUI程序结构与运行机制
1.1.1 GUI项目文件组成
项目包含文件:
-
项目配置文件(.pro):qmake构建系统的项目配置文件,存储了项目的设置内容。
-
UI文件(.ui):用于窗口界面可视化设计的文件。
-
主程序文件(main.cpp):包含main()函数的源文件。
-
窗口类定义头文件与源文件(.h、.cpp):继承于项目创建时选择的基类的类的头文件和源文件。
1.1.2 项目配置文件
qmake根据项目配置文件(qmake配置文件)生成makefile文件,然后C++编译器根据Makefile文件进行编译和链接。对于Qt项目,qmake还会自动为元对象编译器(meta-object compiler,MOC)和用户界面编译器(user interface compiler,UIC)生成构建规则。
qmake配置文件中常见的变量
变量 |
含义 |
QT |
项目中使用的Qt模块列表 |
CONFIG |
项目的通用配置选项 |
DEFINES |
项目中的预处理定义列表 |
TEMPLATE |
项目使用的模版,项目模版可以是应用程序(app)或库(lib),默认为app |
SOURCES |
项目中的源文件列表 |
HEADERS |
项目中的头文件列表 |
FORMS |
项目中的UI文件列表 |
TARGET |
项目构建后生成的应用程序的可执行文件名称,默认为项目名称 |
DESTDIR |
目标可执行文件的存放路径 |
INCLUDEPATH |
项目用到的其他头文件的搜索路径列表 |
DEPENDPATH |
项目用到的其他依赖文件(如源文件)的搜索路径列表 |
qmake提供替换函数,用于在配置过程中访问变量或内置函数的值,“$$”是替换函数的前缀
$${变量名|内置函数名}
$$变量名|内置函数名
Qt类库以模块的形式组织各种功能的类,根据项目涉及的功能需求,在项目中添加对应的类库模块支持。
Qt += 模块名 ...
条件执行加入模块,大于指定版本才加入指定模块。
greaterThan(Qt_MAJOR_VERSION,版本号) += 模块名 ...
后面的SOURCES、HEADERS、FORMS记录了项目中包含的源程序文件、头文件和窗体文件的名称。
SOURCES += cpp文件名 ...
HEADERS += 头文件名 ...
FORMS += 窗体文件名 ...
这些文件列表是Qt Creator自动添加到项目管理文件中的,不需要手动更改。
1.1.3 UI文件
UI文件是用于窗口界面可视化设计的文件,双击其Qt Creator会打开内置的Qt Designer对窗体界面进行可视化设计。
Qt Designer的功能区域:
-
组件面板。位于左侧,分为多个分组,Layout、Spacers、Buttons等。
-
待设计的窗体。位于中间,若要将某个组件放置到窗体上,从组件面板上拖动一个组件放置到窗体上。
-
Action编辑器(Action Editor)和信号与槽编辑器(Signals and Slots Editor)。位于下方,Action编辑器用于可视化设计Action;信号与槽编辑器用于可视化进行信号与槽的关联。
-
布局和界面设计工具栏。位于上方,工具栏上的按钮主要实现布局和界面设计。
-
对象检查器(Object Inspector)。位于右上方,用树状视图显示窗体上各组件的布局和层级关系,视图有两列,显示每个组件的对象名和类名。
-
属性编辑器(Property Editor)。位于右下方,显示某个选中的组件或窗体的各种属性,可以修改这些属性的值。最上方显示文字为“对象名:类名”,下面的内容分为两列,Property列是属性名,Value列是属性值。属性根据类又分为多个组,实际上表示了类的继承关系,下面的类继承于上面的类。objectName属性值表示组件的对象名,每个组件都需要一个唯一的对象名。
通常把处于设计阶段的UI称为窗体(form),处于运行阶段的UI称为窗口。
1.1.4 主程序文件
主程序文件main.cpp中有main()函数
#include "widget.h"
#include <QApplication>
int main(int argc,char *argv[])
{
QApplication a(argc,argv); //定义并创建应用程序
Widget w; //定义并创建窗体对象
w.show(); //显示窗体
return a.exec() //运行应用程序,并进入消息循环和事件处理
}
1.1.5 窗口相关的文件
窗口界面设计和界面组件的事件处理是GUI程序设计的主要任务。
|
|
widget.h |
窗口类Widget的头文件 |
widget.cpp |
实现Widget类的源文件 |
widget.ui |
窗口UI文件,用于在Qt Designer中进行窗口界面可视化设计。.ui是一个XML文件,存储了可视化窗口界面上各个组件的属性、布局、信号与槽的关联。 |
ui_widget.h |
UI文件经过UIC编译后的,根据可视化窗口界面上的组件及其属性、布局和信号与槽的关联等生成的一个窗口UI类Ui_Widget的头文件 |
-
widget.h文件
窗口类Widget的头文件。在创建项目时,选择窗口类的基类为QWidget,并命名其为Widget。
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif
(1)namespace声明
namespace Ui { class Widget; }
声明了一个名为Ui的命名空间,其中声明一个类Widget,但是这个Widget类不是本文件中定义的Widget类,而是ui_widget.h文件中定义的类(完全继承于窗口UI类Ui_Widget)。
Ui::Widget<=>Ui_Widget
(2)窗口类Widget类的定义
一个继承于QWidget类的类Widget的定义,也就是主程序创建的窗口类。
宏Q_OBJECT,这是使用Qt元对象系统(包括信号与槽机制)的类都必须插入的一个宏。
在public部分定义了Widget类的构造函数和析构函数。
在private部分又定义了一个指向窗口UI类的指针。
Ui::Widget *ui;
-
widget.cpp文件
实现Widget类的源文件。
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent):QWidget(parent),ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
(1)包含头文件部分自动加入了如下的一行包含语句:
#include "ui_widget.h"
这个就是UI文件widget.ui被UIC编译后生成的与其对应的窗口UI类Ui_Widget的头文件。
(2)构造函数
构造函数头:
Widget::Widget(QWidget *parent):QWidget(parent),ui(new Ui::Widget)
执行父类QWidget的构造函数,初始化ui指针指向一个Ui::Widget类的对象。在Widget类里访问Ui::Widget类的成员变量或函数就是通过Widget类中的ui指针。
构造函数体只有一行语句:
ui->setupUi(this)
执行Ui::Widget类的setupUi()函数,实现以Widget窗口为父容器创建可视化窗口界面上所有的组件,并设置其属性、布局和信号与槽的关联。
-
widget.ui文件
窗口UI文件,一个XML文件,存储了可视化窗口界面上各个组件的属性、布局、信号与槽的关联。用Qt Designer可视化设计的窗口界面都由Qt自动解析并以XML文件的形式保存下来。
-
ui_widget.h文件
在构建项目时,UI文件被Qt的UIC编译后生成的窗口UI类Ui_Widget的头文件。
ui_widget.h文件并不会出现在Qt Creator的项目管理目录树中,它是构建项目时的一个中间文件,可以选择手动添加到项目中。
/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 6.4.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_Widget
{
public:
QLabel *labDemo;
QPushButton *btnClose;
void setupUi(QWidget *Widget)
{
if (Widget->objectName().isEmpty())
Widget->setObjectName("Widget");
Widget->resize(271, 162);
QFont font;
font.setFamilies({QString::fromUtf8("Widget")});
font.setPointSize(10);
font.setBold(true);
Widget->setFont(font);
labDemo = new QLabel(Widget);
labDemo->setObjectName("labDemo");
labDemo->setGeometry(QRect(50, 40, 201, 35));
QFont font1;
font1.setFamilies({QString::fromUtf8("labDemo")});
font1.setPointSize(20);
font1.setBold(true);
labDemo->setFont(font1);
btnClose = new QPushButton(Widget);
btnClose->setObjectName("btnClose");
btnClose->setGeometry(QRect(160, 105, 81, 31));
retranslateUi(Widget);
QObject::connect(btnClose, &QPushButton::clicked, Widget, qOverload<>(&QWidget::close));
QMetaObject::connectSlotsByName(Widget);
} // setupUi
void retranslateUi(QWidget *Widget)
{
Widget->setWindowTitle(QCoreApplication::translate("Widget", "First Demo", nullptr));
labDemo->setText(QCoreApplication::translate("Widget", "Hello Qt6", nullptr));
btnClose->setText(QCoreApplication::translate("Widget", "Close", nullptr));
} // retranslateUi
};
namespace Ui {
class Widget: public Ui_Widget {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_WIDGET_H
(1)定义了一个类Ui_Widget,用于封装可视化设计的窗口界面。
Ui_Widget类本质不是窗体类,只是一种实现可视化窗口界面的窗口UI类。
(2)在public部分为窗口界面上每个组件声明了一个指向该组件的指针数据成员,成员名为可视化设计时设置的对象名。
QLabel *LabDemo;
QPushButton *btnClose;
(3)在public部分定义了一个成员函数setupUi(),用于以Widget窗口为父容器创建可视化窗口界面上所有的组件,并设置其属性、布局和信号与槽的关联。
函数头:
void setupUi(QWidget *Widget)
函数体:
-
根据可视化窗口界面,以Widget窗口为父容器创建各个组件,并设置其属性。
labDemo = new QLabel(Widget);
btnClose = new QPushButton(Widget);
-
调用函数retranslateUi(Widget),用来设置各个组件的文字内容属性。在设计多语言界面时会用到。
-
设置信号与槽的关联
QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close()));
//调用connect()函数,将在信号与槽编辑器中设置的信号与槽的关联转换为实现代码。
QMetaObject::connectSlotsByName(Widget);
//设置槽函数的关联方式,用于将可视化自动生成的组件信号的槽函数与其组件信号相关联。
(4)定义了一个命名空间Ui,并在其中定义了一个完全继承于类Ui_Widget的类Widget。
namespace Ui {
class Widget: public Ui_Widget {};
}
ui_widget.h文件里实现可视化窗口界面功能的类是Ui_Widget。再定义一个类Widget从Ui_Widget继承而来,并定义在namespace Ui里,这样Ui:: Widget与widget.h里的类Widget同名,但是用namespace区分开来。所以,界面的Ui:: Widget类与文件widget.h里定义的Widget类实际上是两个类,但是Qt的处理让用户感觉不到Ui::Widget类的存在,只需要知道在Widget类里用ui指针可以访问可视化设计的窗口界面组件就可以了。
1.2 可视化UI设计
1.2.1 界面组件属性设置
(1)对象名是界面组件的唯一标识。在程序中访问界面组件时都是通过其对象名进行的,自动生成的槽函数的名称里也有对象名。所以组件的对象名设置好之后一般不要改动。若需要修改,涉及的代码需要相应的改动。
(2)窗体对象名(与窗口类名同名)与窗口UI类名有关。所以,一般在UI设计器中不要修改窗体对象名。若重命名窗体对象名,则需要重命名所有窗口UI类名。
(3)设置窗体的font属性后,界面上其他组件的默认字体为窗体的字体。
(4)组件的属性都有默认值,默认属性值被修改后,属性编辑器里的其属性名会以粗体显示。若要恢复默认值,点击属性值右端的还原按钮。
1.2.2 界面组件布局管理
布局:界面上组件的排列方式。使用布局可以使组件有规则地分布,并且使其随着父容器大小变化自动调整大小和相对位置。
-
界面组件的层次关系
为了将界面上的各个组件的分布设计得更加美观,经常使用一些容器类组件。将组件放入容器类组件中,移动容器组件就会同时移动内部的组件,选择容器组件也就同时选择内部的组件。称容器为内部组件的父容器,窗体是所有组件的顶层容器。
-
布局管理
Qt为界面设计提供了丰富的布局管理功能。
-
组件面板中有Layouts和Spacers两个分组。
|
|
![]() Vertical Layout |
垂直方向布局,组件自动在垂直方向上分布 |
![]() Horizontal Layout |
水平方向布局,组件自动在水平方向上分布 |
![]() Grid Layout |
网格布局,网状布局大小改变时,每个网格的大小都改变。 |
![]() Form Layout |
表单布局,与网格状布局类似,但是只有最右侧的一列网格会改变大小,适用于只有两列组件时的布局。 |
![]()
|
用于水平分隔的非可视组件 |
![]()
|
用于垂直分隔的非可视组件 |
-
上方的工具栏中有设计状态和布局管理的各种按钮。
按钮及快捷键 |
功能 |
![]() Edit Widget(F3) |
进入编辑状态,就是正常的设计状态 |
![]() Edit Signals/Slots(F4) |
进入信号与槽的可视化设计状态 |
![]() Edit Buddies |
进入伙伴关系编辑状态 |
![]() Edit Tab Order |
进入Tab顺序编辑状态 |
![]() Lay Out Horizontally(Ctrl+H) |
将窗体上所选组件水平布局 |
![]() Lay Out Vertically(Ctrl+L) |
将窗体上所选组件垂直布局 |
![]() Lay Out Horizontally in Splitter |
将窗体上所选组件用一个分割条进行水平分割布局 |
![]() Lay Out Vertically in Splitter |
将窗体上所选组件用一个分割条进行垂直分割布局 |
![]() Lay Out in a Form Layout |
将窗体上所选组件表单布局 |
![]() Lay Out in a Grid |
将窗体上所选组件网格布局 |
![]() Break Layout |
解除窗体上所选组件的布局,也就是打散现有的布局 |
![]() Adjust Size(Ctrl+J) |
自动调整所选组件的大小 |
在窗体上选择组件的同时按住Ctrl键,可以实现组件多选。
-
伙伴关系与Tab顺序
伙伴关系:界面上一个标签(Label)组件和一个具有输入焦点的组件相关联的关系。可以在程序运行时,使用快捷键(Alt+快捷字符)将输入焦点切换到对应的组件上。
在工具栏上单击“EditBuddies”按钮进入伙伴关系编辑状态,按住一个标签拖向一个组件释放,就建立了标签和组件之间的伙伴关系。设置标签的text属性为“…(&快捷字符)”,其中符号“&”用来指定快捷字符,界面上并不显示“(&快捷字符)”。
Tab顺序:在程序运行时,按下Tab键时输入焦点的移动顺序。
在工具栏上单击“EditTab Order”按钮进入Tab顺序编辑状态,在界面上会显示具有Tab顺序的组件的Tab顺序编号,按希望的顺序依次单击组件,就可以重排Tab顺序了。没有输入焦点的组件是没有Tab顺序的。
1.2.3 信号与槽
信号与槽是Qt编程的基础,也是Qt的一大创新。有了信号与槽的编程机制,在Qt中处理界面上各个组件的交互操作就变得比较直观和简单。
信号(Signal):在特定情况下被发射的通知。
槽(Slot):对信号进行响应的函数,也称为槽函数。槽函数可以与信号关联,当信号被发射时,关联的槽函数被自动执行。
信号与槽关联是用函数QObject::connect()实现的
[QObject::]connect(<sender>,SIGNAL(signal(<signal parameters>)),<receiver>,SLOT(slot(<slot parameters>)));
connect()函数是Object类的一个静态函数,而QObject是大部分Qt类的基类,在实际调用时可以省略前面的限定符。
其中,sender是发射信号的对象名,signal是信号名。信号可以看做是特殊的函数,有参数时还需要指明参数类型,该参数将会传给槽函数的参数;receiver是接收信号的对象名,slot是槽函数名,有参数时还需要指明参数类型。SIGNAL和SLOT是Qt的宏,分别用于指明信号和槽函数,并将它们的参数转换为相应的字符串。
信号与槽的使用规则:
(1)一个信号可以连接多个槽函数。当一个信号与多个槽函数关联时,信号发射,槽函数按照关联顺序依次执行。
(2)多个信号可以连接同一个槽函数。
(3)一个信号可以连接另外一个信号,这样当一个信号发射时,另一个信号也发射。
[QObject::]connect(<sender>,SIGNAL(signal(<signal parameters>)),<receiver>,SIGNAL(signal(<signal parameters>)));
(4)信号的参数个数不能少于槽的参数个数。
(5)在使用信号与槽的类中,必须在类的定义中插入宏Q_OBJECT。
1.2.4 可视化信号与槽关联
-
信号与槽编辑器
关联组件内建的信号与槽。在信号与槽编辑器的工具栏上单击“Add”按钮,出现一个条目,分别选择sender,signal,receiver,slot。
信号与槽编辑器中信号与槽的关联是在窗口UI类中的setupUi()函数里调用connect()函数实现的。
-
工具栏Edit Signals/Slots
关联组件内建的信号与槽。单击工具栏里的“Edit Signals/Slots”按钮,窗体进入信号与槽的可视化设计状态,按住一个组件移动到另一个组件上释放,出现关联设置对话框,左边列表框显示一个组件的信号,右边列表框显示另一个组件的槽函数,分别选择,单击“OK”按钮。在信号与槽编辑器将会对应添加该信号与槽的关联。
1.2.5 为组件的信号生成槽函数原型和框架
为一个组件的信号关联自定义的槽函数。选中一个组件,单击右键调出快捷菜单,单击“Go to slot…”,选择一个组件的信号,然后单击“OK”按钮,在窗口类的定义中,在private slots部分自动生成一个槽函数原型,函数名根据发射信号的对象名和其信号名自动命名。
void on_<object name>_<signal name>(<slot parameters>);
同时,在窗口类的源文件中,自动生成该槽函数的框架。
void on_<object name>_<signal name>(<slot parameters>){
}
生成的槽函数与信号关联是在窗口UI类中的setupUi()函数里设置槽函数的关联方式实现的,相当于对应信号与槽的关联语句。
[QObject::]connect(<object name>,SIGNAL(signal(<signal parameters>)),
this,SLOT(on_<object name>_<signal name>(<slot parameters>)));
1.3 代码化UI设计
1.3.1 窗口类声明
(1)在private部分为窗口上每个组件声明一个指向该组件的指针数据成员,成员名作为组件的唯一标识。
组件类名 *组件名;
(2)在private部分声明两个函数原型。
void iniUI();
//实现创建所有窗口组件,并设置属性和布局
void iniSignalSlots();
//实现所有信号与槽的关联
(3)在private slots部分声明槽函数。
void slot(<signal parameters>);
1.3.2 窗口组件的创建与布局
(1)窗口组件的创建与布局,以及信号与槽函数的关联都在窗口类的构造函数中完成。
窗口类名::窗口类名(QWidget *parent) : 窗口基类(parent)
{
//ui->setupUi(this)
iniUI(); //窗口组件的创建与布局
iniSignalSlots(); //信号与槽函数的关联
//setWindowTitle("UI created mannually");
}
如果有用到可视化设计,iniUI()函数调用一定要在ui->setupUi(this)之后,因为iniUI()函数时在可视化创建的窗口基础上添加其他组件。
(2)iniUI()函数实现窗口组件的创建与布局。
void 窗口类::iniUI()
{
//窗口组件创建、属性设置
组件名=new 组件类名(实参列表);
组件名->属性设置函数名(实参列表);
//窗口组件布局
//布局组件创建、向布局添加组件
布局组件类名 *布局组件名=new 布局组件类名;
布局组件名->添加组件函数(组件名);
...
//设置窗口的主布局
setLayout(布局组件);
}
1.3.2 信号与槽的关联
iniSignalSlots()函数实现信号与槽的关联。
void QWDlgManual::iniSignalSlots()
{
[QObject::]connect(sender,SIGNAL(signal([<signal parameters>)]),receiver,SLOT(slot([<slot parameters>])));
...
}
1.4 混合方式 UI设计
可视化UI设计无需人工编写代码去处理大量繁琐的界面组件的创建和布局管理,可以直观地进行界面设计,可以大大地提高工作效率。但是可视化UI设计也有一些缺陷,例如某些组件无法可视化的添加到界面上,而用代码就可以。
采用纯代码方式进行UI设计虽然无所不能,但是设计效率太低,过程非常繁琐。所以,能用可视化设计的就用可视化设计解决,无法解决的再用代码解决,将两种方法结合,才是高效设计UI的方法。
1.4.1 创建资源文件
在Qt Creator里单击“File”→“New File…”菜单项,在新建文件对话框里选择“Qt”→“Qt Resource File”,然后按照向导的指引设置资源文件的文件名,选择添加到的项目,在项目管理目录树里会自动创建一个Resources文件夹,资源文件都会保存于此。
资源文件最主要的一个功能就是存储图标和图片文件,以便在程序里使用,在资源文件节点上右击,在弹出的快捷菜单中选择“Open in Editor”菜单项,打开资源文件编辑器。在资源文件里创建一个前缀(Prefix),前缀表示资源的分组,在下方的功能区单击“Add Prefix”按钮,设置一个前缀名。然后再单击“Add Files”选择图标文件即可。如果所选的图标文件不在本项目的子目录里,会提示复制文件到项目的子目录下。
1.4.2 设计Actions
在Action编辑器的工具栏上单击“New”按钮或在Action列表中双击Action,出现编辑对话框:
-
Text:显示文字,作为菜单项标题或工具栏按钮标题显示。若该标题后面有省略号,则在工具栏按钮上显示时会自动忽略省略号。
-
Object name:该Action的objectName。命名法则:以“act”开头。如果一个界面上Action比较多,还可以分组命名,以“act分组名”开头。
-
ToolTip:当鼠标在一个菜单项或工具栏按钮上停留时出现的提示文字。
-
Icon:显示图标,作为工具栏按钮图标显示,单击其右边的按钮可以从资源文件里选择图标,或者直接选择图片文件作为图标。
-
Checkable:是否可以被复选。
-
Shortcut:设置快捷键,选中编辑框,然后按下想要设置的快捷键。
单击“OK”按钮就可以新建或修改Action了。所有用于菜单和工具栏设计的功能都需要用Action来实现。
1.4.3 设计菜单和工具栏
窗体类QMainWindow,具有菜单栏、工具栏和状态栏。菜单栏位于窗体的最上方,工具栏位于菜单栏下方,状态栏位于窗体都最下方。
-
创建菜单分组
在菜单栏上双击“Type Here”,出现编辑框,输入所要创建菜单分组的名称,然后回车。
-
创建菜单项和分隔条
(1)创建菜单项
从Action编辑器的列表中将一个Action拖放到菜单分组下。
(2)创建分隔条
在菜单分组中双击“Add Separator”。
-
创建工具栏按钮和分隔条
(1)创建工具栏按钮
从Action编辑器的列表中将一个Action拖放到工具栏中。
(2)创建分隔条
在工具栏上单击右键,单击“Append Separator”。
-
创建多个工具栏
在窗体上单击右键,单击“Add Tool Bar”。
-
设置工具栏按钮的现实方式
设置工具栏的toolButtonStyle属性,类型为Qt::ToolButtonStyle枚举,缺省为Qt::ToolButtonIconOnly,即只显示按钮图标。
该枚举的枚举值有:
-
Qt::ToolButtonIconOnly——只显示按钮图标。
-
Qt::ToolButtonTextOnly——只显示文字。
-
Qt::ToolButtonTextBesideIcon——文字显示在按钮右边。
-
Qt::ToolButtonTextUnderIcon——文字显示在按钮下方。
1.4.4 Action的功能实现
-
Action的信号
(1)triggered():在单击菜单项或工具栏按钮时发射。
(2)triggered(bool):在单击菜单项或工具栏按钮时发射,并将Action的复选状态属性checked作为参数传递。
-
Action的属性
(1)enabled:类型为bool
true——按钮正常显示、使用。
false——按钮变灰显示,无法使用。
(2)checked:类型为bool
ture——按钮选中状态显示。
false——按钮非选中状态显示。
1.4.5 为应用程序设置图标
将一个图标文件(.ico)放到项目根目录下,在项目配置文件里设置:
RC_ICONS = 图标文件名.ico
构建项目,生成的可执行文件以及窗口的图标就会换成设置的图标。
1.5 Qt项目构建的基本原理
Qt项目的构建基本过程

1.5.1 元对象系统和MOC
Qt对标准C++进行了扩展,引入了元对象系统(meta-object system,MOS)(在Qt Creator中编写程序时使用的C++,实际上是经过Qt扩展的C++),所有从QObject继承的类都可以利用元对象系统提供的功能。元对象系统支持属性、信号与槽、动态类型转换等特性。
在构建项目时,项目中的窗口类头文件会被元对象编译器(MOC)预编译生成元对象代码文件(moc_窗口类名.cpp)。
1.5.2 UI文件和UIC
在构建项目时,项目中的窗口UI文件会被用户界面编译器(UIC)预编译生成窗口UI类头文件(ui_窗口类名.h)。
1.5.3 资源文件和RCC
在构建项目时,项目中的资源文件会被资源编译器(RCC)预编译生成资源源文件(qrc_资源文件名.cpp)。
1.5.4 标准C++编译器
标准C++编译器就是开发套件中的编译器,使用MOC、UIC、和RCC编译各原始文件的过程属于预编译过程,预编译后生成的是标准C++语言的程序文件,它们经过标准C++编译器编译和链接,最终生成可执行文件。
1.6 使用CMake构建系统
CMake是一个功能强大的跨平台的构建工具,它通过与平台和编译器无关的配置文件控制软件的构建过程,生成本地化的makefile文件或IDE项目。
1.6.1 CMake项目配置文件
CMakeLists.txt是CMake构建系统的项目配置文件(CMake项目文件),是用CMake语言写的;CMake Modules是项目用到的其他一些CMake模块,具体的模块就是一些后缀为“.cmake”的文件。
cmake_minimum_required()函数设置要求的CMake最低版本。
cmake_minimum_required(VERSION 版本号) #需要的CMake最低版本
project()函数设置项目名、版本号和使用的编程语言
project(项目名 VERSION 版本号 LANGUAGES CXX) #编程语言是C++
set()函数设置CMake变量的值
set(变量名 值)
find_package()函数查找和导入Qt中的模块,target_link_libraries()函数设置链接时用到的Qt模块。
find_package(Qt6 COMPONENTS 模块名 REQUIRED) #查找Qt6,导入指定块
target_link_libraries(项目名 PRIVATE Qt6::模块名)
qt_add_executable()函数创建可执行文件,并设置依赖的头文件、源文件、UI文件和资源文件。
qt_add_executable(可执行文件名 #创建可执行文件
MANUAL_FINALIZATION #可选参数,手动结束创建目标的过程
main.cpp #项目的依赖文件列表
窗口类源文件名
窗口类头文件名
UI文件名
资源文件名
)
qt_finalize_executable()函数结束创建目标的过程。
qt_finalize_executable(可执行文件名) #结束生成可执行文件的过程
CMake项目文件的内容一般不需要我们手动修改,项目中新增文件时会自动更新依赖文件列表。只有用到Qt的某个模块时,才需要编写find_package()和target_link_libraries()函数。
1.6.2 CMake项目构建
新建文件结束向导后,Qt Creator会提示文件不会被自动添加到CMakeLists.txt文件里,需要手动添加。
set(PROJECT_SOURCES #设置变量 PROJECT_SOURCES 等于下面的列表
main.cpp #项目的依赖文件列表
窗口类源文件名
窗口类头文件名
UI文件名
新建文件名 #手动添加文件
)
使用CMake管理Qt项目时,Qt Creator的Project设置里没有Shadow build复选框,但构建项目后总是在项目的同级目录下生成对应版本的文件夹(输出文件夹)保存构建后的文件。输出文件夹的子目录项目名_autogen里有MOC、UIC和RCC预编译生成的文件。
1.7 Qt Creator使用技巧
1.7.1 文本编辑器使用技巧
Qt Creator中的文本编辑器提供了一些常用的操作,通过文本编辑器的快捷菜单或Qt Creator的菜单可以实现这些操作。熟悉这些操作的快捷键,可以提高工作效率。
|
|
|
Switch |
F4 |
在同名的 |
Follow |
F2 |
跟踪光标下的符号,若是变量名,可跟踪到变量声明的地方;若是函数原型或函数头的函数名,可在两者之间切换 |
Switch Between Function Declaration and Definition |
Shift+F2 |
在函数原型和函数定义之间切换 |
Refactor->Rename Symbol Under Cursor |
Ctrl+Shift+R |
对光标处的符号更改名称,这将替换所有用到这个符号的的地方 |
Refactor->Add Definition in .cpp |
|
为函数原型在源文件里生成函数定义 |
Auto-indent Selection |
Ctrl+I |
自动进行缩进选择的代码段 |
Toggle Comment Selection |
Ctrl+/ |
注释或取消注释 |
Context Help |
F1 |
为光标所在的符号显示帮助文件的内容 |
Save All |
Ctrl+Shift+S |
保存所有文件 |
Find/Replace |
Ctrl+F |
调出查找/替换对话框 |
Find Next |
F3 |
查找下一个 |
Build |
Ctrl+B |
构建当前项目 |
Start Debugging |
F5 |
开始调试 |
Step Over |
F10 |
调试状态下单步略过,即执行当前行程序语句 |
Step Into |
F11 |
调试状态下跟踪进入,即若当前行里有函数,就跟踪进入函数体 |
Toggle Breakpoint |
F9 |
设置或取消当前行的断点设置 |
在使用Qt时,要善于使用Qt自带的帮助文件,对于一个编程语言或类库来说,其自带的帮助文件是最全面最权威的资料。在Qt Creator窗口左侧的工具栏上有“Help”按钮,单击可以打开Qt的帮助文件系统,也可以使用Qt程序组里的Assistant单独打开帮助系统。
在帮助文件显示界面上,左上方工具栏上有个下拉列表框,可以选择Bookmarks、Contents、Index、Search四种模式。
-
Bookmarks模式下,左边框里显示已存储的书签,任何帮助页面下,点击窗口上方工具栏上的“Add Bookmark”可以添加书签。
-
Contents模式下,左边框里以目录树形式显示Qt的所有模块,可以分类浏览想看的内容。
-
Index模式下,可以输入查找内容,左边框里会列出与输入内容前匹配的帮助主题列表。
-
Search模式下,可以输入关键字进行搜索。
1.7.2 项目管理
在项目管理目录树中,项目节点的快捷菜单中:
-
Build:以增量方式构建项目。
-
Rebuild:完全重新构建项目。
-
Clean:清楚项目构建过程中的所有中间文件。
-
Run qmake/CMake:使用qmake/CMake重新构建项目。