1.前言
前面一章,我们简单学习了一下Qt库的使用,以及一个程序的开发流程:就是不停的调库。
这就是Qt之所以非常火的原因,因为其自带的库真的太多了,绝大多数时候都无需我们自己造轮子。
但除此之外,Qt另一个非常火的原因就是其界面的强大,前面我们使用MFC的时候可以看到,其实我们对界面的美化,能控制的范围相当有限,想要变成好看,非常难。
但Qt不一样,你可以非常容易的,将界面做的很好看,所以本章就将开始介绍Qt的界面开发流程。
2.创建项目
暂时我们还是使用qtcreator来开发Qt项目,虽然不好用,但好歹是官方自带的,得熟悉一下。
这次我们不再创建控制台项目了,而是要创建一个窗口项目:

即Qt Widgets,widget意为部件,因为在Qt里面,几乎所有东西都是部件,可以任意搭配缝合,窗口应用程序更是尤为突出。
然后随便取个项目名:

依旧选择qmake:

这里注意一下,要选择QWidget,即创建最原始的部件窗口:

它可以任意扩展为你想要的类型对话框,以后你熟悉了之后,当然也可以选择其它两种已经初始化好的对话框:

- QMainWIndows:一个所有部件都已经具备的窗口,比如状态栏,菜单栏等等,继承自- QWidget。
- QDialog:对话框窗口,可以很方便弹出,继承自- QWIdget。
然后,这次我们就不选语言了:

编译套件还是选择msvc:

汇总这里的版本控制系统也不需要,直接完成:

然后我们就能看到自动生成的项目文件:

这里你可能首先就会感到奇怪,明明是窗口程序,为啥是main函数?不应该是winMain函数吗?
这就是Qt的独到之处了,Qt将各种类型项目的差异性封装了起来,统一使用一个main函数!
当你的项目文件(.pro)中,没有添加CONFIG+=cmdline时,它就会链接对应的含有winMain函数的库,那个winMain才是真正的入口点,只不过在那个winMain函数里面,调用了我们这里的main函数。
所以不是没有使用winMain,而是它被隐藏了!
生成的文件之内的,我们也先不用管,直接点击运行一下试一试!

与MFC一样,我们啥都没干,就可以生成一个窗口程序。
但相比较而言,你会发现Qt的代码非常简洁,文件很少,而MFC里面却是各种文件混杂,莫名其妙的代码一大堆。
这便是Qt的优势之处:简洁明了。
3.文件与代码详解
首先来看,这个生成的项目中有哪些文件,都是干什么的:

- main.cpp:程序源码,存放了入口函数代码。
- widget:主窗口类,包含- widget.h与- widget.cpp这两个文件,用于控制主窗口的行为。
- widget.ui:UI设计文件,和MFC类似,通过拖拽即可完成界面搭建。
然后我们来具体看一看文件中的代码,首先是main.cpp文件:

代码相当简单,首先和前面控制台一样,也是声明了一个应用类,不过这个类名为QApplication,它是QCoreApplication的子类。
声明出对象后,然后调用exec函数进入消息循环:
    QApplication a(argc, argv);
    return a.exec();
只不过在中间,还实例化了一个窗口类,并调用show函数显示出来,这个操作学过win api界面编程的我们应该很清楚,其实就是在调用showwindow这个win API函数来显示窗口。
    Widget w;
    w.show();
这个类就是我们的主窗口类,在生成的文件widget.h与widget.cpp中。
然后我们就来看看这个主窗口类干了些什么:

首先是最外层的宏定义,这是为了防止多次包含:
#ifndef WIDGET_H
#define WIDGET_H
 //省略中间代码
#endif // WIDGET_H
然后是头文件:
#include <QWidget>
这是Qt提供的标准窗口组件类,我们这个主窗口类,就是通过继承它,才能直接使用各种各样的窗口函数,这与MFC一样,都是通过继承实现的。
然后是一个看起来比较奇怪的东西:
QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
这两个其实是空宏,给我们程序员看的,大致意思可能就是这块地方专门用于使用命名空间,没啥作用,有用的是里面的东西:
namespace Ui { class Widget; }
这代表着,将类Widget添加到命名空间Ui中。
然后来到主要部分,就是继承QWidget即可。
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
private:
    Ui::Widget *ui;
};
这里有个宏Q_OBJECT很重要,因为Qt里面最重要的机制就是信号与槽,虽然暂时还没讲怎么用,但你需要知道的是,如果类想要使用这个机制,就必须写上这个宏!
然后构造函数与析构函数就不多说了,我们可以看到它还有一个很奇怪的操作,就是定义了一个自己的成员指针变量,但名字却写成ui
 Ui::Widget *ui;
这句话的意思应该都能看懂吧!就是命名空间Ui中的Widget声明一个指针变量。
它是在前面添加到这个命名空间中的,这不就相当于自己里面声明了一个自己类型的指针变量嘛!这有啥用?
但先不用急,我们再来看看实现文件:

这里只有构造函数与析构函数,构造函数很简单,这种赋值方式以前应该提到过,这种叫做初始化列表,理论上比在函数体内赋值更快,更详细的内容可以参照这篇文章:初始化列表。
比如这里:
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}
QWidget(parent)就是调用的父类构造函数,将该参数交给父类处理,然后成员函数指针ui则是new了一个对象:
ui(new Ui::Widget) //等价于 ui=new Ui::Widget;
最离奇的事情是,下面这句:
 ui->setupUi(this);
是不是感觉有几个问号???
自己这个类里面声明了一个自己的对象指针,然后这个对象指针调用一个函数将自己的指针传进去。
简直魔幻!这有啥用?意义何在?
setupUi这个函数,看名字应该是绑定这个类的UI的,但这里却传进去了自己!UI用什么表示的呢?UI与类是如何绑定在一起的呢?
析构函数就不用说了,只是delete这个指针。
之所以感到魔幻,只是因为我们还没有看完代码,最后我们再来看一看这个ui_widget.h做了些什么事情。

ui_widget.h:这是一个UI类,用于控制UI的,相当于上面的窗口类与我们看到的界面的桥梁连接。
