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的,相当于上面的窗口类与我们看到的界面的桥梁连接。
此时你可以按住键盘上的Ctrl
键,然后鼠标左键点击这个头文件,就可以跳转过去:
这里是一个类,并且我们看到了一个函数,setupUi
!,这不就是刚才调用的那个函数嘛!
然后再看到下面的:
namespace Ui {
class Widget: public Ui_Widget {};
} // namespace Ui
这下就一下子明悟了,原来是使用的多继承!
所谓多继承,就是Widget
原本就已经继承了QWidget
,而现在又继承了Ui_Widget
,相当于它一个类里面拥有了两个父类的东西!
至于这个类里面具体干了些什么,可以看到里面调用了一些我们常见的函数。
比如
resize
即改变窗口大小,我们就大概能猜到,这个类才是真正用来控制UI的。
但这个类又是如何绑定我们界面的呢?
这就不用管了!Qt内部机制既然已经帮我们封装好了,我们又何必自讨没趣。
我们只需要知道,这个类是Qt自动用来控制控件的,我们并不用管,并且Qt还使用了多继承,仿佛是特意想要将这个类给隐藏了(实际上是通过多继承的方式在使用这个类)
至此,我们就把Qt的代码过了一遍,看上去是不是比MFC
简单多了!短短几行代码就能轻易生成一个窗口!而且还相当简洁!
4.初步学习使用UI
在大致了解了这些文件与代码的用途以及逻辑之后,我们就可以正式写代码了!
首先是设计界面,这个很简单,只需要双击下面这个文件:
然后就会自动弹出与MFC类似的界面
然后左边便是Qt里面的控件,操作和MFC相同,比如我们可以拖一个按钮到窗口上:
Qt里面的按钮名称为Push Button,然后根据我们使用MFC的逻辑,下一步肯定就是更改这个控件的属性了!
界面的右下方就显示的是当前控件的属性:
将这些内容折叠后,我们就可以很清楚的看到继承关系,从上到下,我们这个按钮类QPushButton
已经是第四层了。
也正因为是继承自这些类,所以这些类中的某些属性,只要继承了它,就可以在这里找到,并更改。
首先是QObject
类,这是Qt里面最基层的类,Qt里面所有的类都继承自它。
它有一个属性,就是这个对象的名字!这和MFC里面给控件绑定一个控件变量很类似,不过很显然,这比MFC要方便且简单的多:
在代码中,我们也是通过这个更改后的变量名来控制这个控件的,比如这里我将其更改为了pb_test
。
然后是QWidget
类,它是一个最基层的窗口类,几乎所有控件都继承自它:
可以看到,它的属性就相当多了,虽然都是英文,但都不难,基本能够见名知意,这里不再深入,可以自己稍微研究研究,有个印象即可。
我要提的是上图箭头所指的一个非常重要的属性,当我们想要美化控件时,就可以使用它,非常的方便,就是styleSheet
这个属性。