Qt的概述

1.前言

互联网公司有这样一些核心岗位:1.开发(程序员)。2.测试。3.运维(管理机器)。4.产品经理(非技术岗,主要的职责是提出需求)。开发岗一般有后端开发,前端开发,算法工程师,游戏开发等等。我们当前所讨论的Qt是用于”客户端开发“的,若把前端开发也单独拎出来又可以分为:1.网页前端开发(做网页的)。2.桌面应用开发(做电脑上的应用程序的)。3.移动应用开发(做手机上的应用程序的)。Qt这个技术主要用于桌面应用开发,桌面应用开发也可以叫客户端开发(一般听到前端可能指具体的某个前端,也可能是包罗万象的前端)。客户端开发属于历史悠久的一个事情,最开始苹果电脑推出第一代图形化操作界面系统的时候,客户端开发这样的技术就诞生了,像网页,移动都是后来出现的。

前面反复讨论到了客户端这样一个术语,再来说一下:客户端可认为是直接和用户打交道的这一端的程序,我们平时所接触到的大部分程序其实都属于客户端,比如我们用的chrome、qq音乐、stream等都可以称为客户端。和客户端有个相对应的概念叫服务器,服务器就属于站在客户端背后的那个男人了。我们用户使用程序是为了解决一些实际的问题,但是有些问题得让服务器通过网络在背后给我们做出一些支撑才能去解决,比如用滴滴打车,下单后订单通过网络到滴滴服务器,再由服务器通过网络派发给司机的客户端,这样就完成了一个打车的过程。很明显是服务器站在客户端背后提供了默默的支撑,才能完成整个业务逻辑,解决实际问题。虽然大部分的客户端程序,是需要有背后的服务器做支撑的,但是确实也有些程序,是不需要服务器的。这样的程序比如画图板、计算机等同样也是直接和客户打交道的,虽然不联网也可以称为客户端程序。总的来说客户端这样的概念是非常广泛的,其实前面说的网页、桌面、移动开发也可以统称为客户端开发,但qt技术专指的是桌面应用开发,qt无法开发网页前端,也不能开发移动应用。但是目前qt官方也支持了移动应用的开发了,但是目前还没有听说有啥知名的商用移动应用是qt开发的。客户端开发的重要任务,是编写和用户交互的界面。和用户交互的界面,有两种典型风格:1.命令行界面/终端界面(黑框框),也称为TUI。2.图形化界面,称为GUI。TUI适用于专业软件,给程序员用的;GUI适合普通用户;qt就是用来编写桌面的GUI程序的一套框架。在Windows中编写GUI程序,也是有很多解决方案的,qt只是其中的一种。qt有一个优势,可以跨平台,运行效率也不差。

2.qt背景介绍

qt是一个跨平台的c++图形用户界面应用程序框架,这句话也不难理解,因为我们前面说了qt是开发图形化界面的。这里有个词叫应用程序框架,什么是框架呢?框架本质上是一群大佬发明出来的,方便让咱们普通的程序员写出来的代码比较靠谱,什么意思呢?在软件开发中谈到自由、灵活是个贬义词,因为代码越自由、灵活就意味着越容易出错,保证代码不出错是一个很重要的事情,于是就有大佬发明了框架,框架可以约束程序员不要把代码写的天马行空,其实框架就是在限制程序员的自由,这样可以保证写的代码的下限不要让代码太糟糕。因此图形化界面应该怎么写,qt这个框架已经把大思路梳理好了,按照大框架走,小的细节自己再添加,这样保证代码不会写的太差,这就是框架的含义。我们平时还听过库,库和框架其实有一定的相似性,都是属于大佬把一些代码写好了,让我们直接去使用。但是库是被程序员调用的,也就是程序员是主体,程序员决定什么时候用库及在哪里用等;框架则是框架占主体,也就是框架把大流程梳理好,然后程序员配合框架,完善填充框架中留出的一些细节。

3.搭建 Qt 开发环境

qt开发环境需要安装三个部分:1.c++编译器(指的是gcc,cl.exe这样的东西,不是visual studio这样的东西。编译器!=ide,编译器只是ide调用的一个程序)。2.qt的SDK,SDK叫做软件开发工具包,它里面会包含开发qt程序所用的方方面面的各种工具。比较幸运的是,一般qt的SDK里面已经内置了一个c++编译器,这样不用额外安装了。内置的编译器是mingw,是windows版本的gcc/g++(要是想用vs内置的cl.exe作为编译器也不是不行,但需要配置很多额外的东西,容易出错)。既然有内置,因此具体安装的过程中,需要把对应的c++编译器一起勾选上。3.需要有一个qt的集成开发环境(IDE),业界主要是这样几个:1.qt官方提供的QT Creator。2.Visual Studio。3.Eclipse。第一种方式是最容易入门,最容易上手的方式,可认为是开箱即用,不需要任何额外的配置。虽然QT Creator使用过程中存在不少bug,有些bug非常影响使用体验,但是整体来说用起来挺方便的,比较适合初学者。第二种方式功能更强,但是需要额外的配置更多,更容易出错。第三种方式并非只是java IDE,它本身是一个ide平台,可以搭配不同的插件构成不同的ide。目前Eclipse市场份额受到的冲击很大,重量级工具有JetBrains,轻量级工具有VsCode。这里推荐用第一个,而且Qt Creator也是集成在qt sdk里面的。因此说是安装三个东西,其实只要安装一个qt sdk,另外两个也就都有了。

4.Qt SDK 的下载和安装

但凡需要下载安装一个程序,首选官方网站。Qt 下载官网:http://download.qt.io/archive/qt/。进去后看到这样一个界面:

看到提供了很多版本,最新的是6.11,我们选择5.14,因为最新的东西可能会不太稳定。现在点进去:

小版本无所谓,随便点一个:

进去后下载这个exe程序,这个东西就是我们要下载的qt sdk(但是有些时候访问国外网站可能会不能访问或下载程序速度很慢),下载sdk后,剩下的安装过程就比较容易了:一路next,选mingw7.3.0 64-bit,这是qt sdk中内置的c++编译器:

装好后,要给装好的qt去配置一下环境变量(不是必须的,但建议配置)。

下面说说怎么配置,在搜索菜单搜索环境变量:

下面点开:

看到上面是配置用户变量,下面是配置系统变量。windows也是一个支持多用户的操作系统,但我们平时都是自己用自己的电脑,所以不太关注是用户变量还是系统变量。用户变量就是当前用户会生效,系统变量是所有用户都会生效。所以这里配置用户和系统都可以,反正都是自己在使用。无论用户还是系统变量都有一个特殊的变量叫Path,linux下也有个环境变量叫PATH,输入的命令,就会去PATH下对应的目录中查找对应的程序,windows下也是一样的(比如win+r后输入calc,打开计算机,就是在PATH路径下进行查找的。若也想运行qq也要把路径添加到Path环境变量里)。现在要做的事情是需要把刚才安装好的qt sdk中的某个目录加到Path环境变量中:

这样环境变量就配置完毕了。那为啥要配置这个环境变量?1.让OS/Qt Creater工具,能够找到qt sdk中提供的exe。2.运行qt程序的时候,能够找到对应.dll动态库(环境变量,也可以理解成一种简单的进程间通信机制)。

现在qt sdk安装完毕了,环境变量配置好了,下面看看安装好的东西里有哪些重点关注的内容:

这几个就是qt sdk中自带的一些工具程序,也都是我们在开发中需要关注的。先简单看一下:

第一个是qt助手,这是qt的一份离线的官方文档,方便查询某个类或api等怎么去使用。再看第二个:

这是图形化的通过拖拽来控件来设计界面的工具。再看第三个:

作用是支持国际化,比如有的时候写的程序,是要和国际接轨的。这样允许你单独创建一个语言配置文件,把界面上需要的各种文字,都配置到文件中,并且在文件提前把各种语言的翻译都配置进去,这样就可以一键式切换语言。再下来是一个终端:

里面是可以使用qt sdk的命令。最后一个是qt的集成开发工具:

也是学习qt过程中主要使用的工具。

5.使用Qt Creator新建项目

点开qt creator,点文件处的新建文件或项目:

弹出的对话框让我们选一个具体的项目模板,我们当前阶段选Application(应用程序),也就是如果使用Qt写一个GUI程序,就应该选择这个。右侧也有很多选项,我们要选的是Qt Widgets Application,这就是图形化界面程序的项目模板。接下来选好后点击选择:

可以选择项目名称和项目所在的路径,选好后点击下一步:

Build system是构建系统,意思是通过qt写的程序,涉及到一系列的'元编程'技术。元编程就是通过代码生成代码,就是qt框架会在编译的时候,自动先调用一系列的生成工具,基于你自己写的代码,生成一系列的其它的c++代码,最终编译的代码,也是最后生成的这些代码,其中qt提供的构建工具主要是上面的三种(这里主要使用qmake),选好后点击下一步:

我们使用qt creator创建项目,会自动的生成一些代码出来,生成的代码就包含一个类,Base class那是选择这个自动生成的类的父类是谁。这主要有三个选项:

第一个是完整的应用程序窗口(可以包含菜单栏,工具栏,状态栏等),第二个表示一个控件(chain窗口上的一个具体的元素,比如输入框,按钮,下拉框等),第三个表示一个对话框。我们此处选第二个:

当父类选QWidget后,对应的类名也就变了。注意Widget这个类是项目里自动生成的代码它的类名,QWidget是自动生成的代码所继承的父类(qt中内置的类都是以Q前缀开头)。选完类名后还有Header file和Source file,其实就是我们让它自动生成一个类,这个类代码要包含在一些文件里,这里就涉及到widget.h和.cpp(此处生成的文件名是和类名关联的,这样的关联并非是强制的,但推荐一样)。还有个form file,qt中创建图形化界面的程序有两种方式:1.直接通过c++代码的方式创建界面。2.通过form file,以图形化的方式来生成界面。此处若勾选了Generate form,它会生成一个widget.ui的formfile,此时就可以使用qt designer或直接使用qt creator来编辑这个ui文件从而以图形化的方式快速方便的生成图形化界面。这里勾选上继续点下一步:

这里是选择翻译文件(对应的语言),此处暂时不关注,和国际化相关的。继续点下一步:

这里是选择一下基于哪个编译器的qt sdk来构建后续代码。继续点下一步:

若要提交到git可以选择,选none也可以。最终点完成:

此时一个项目就创建好了。创建好后看到qt creator生成了一些目录和文件:

我们先运行一下程序感受一下刚刚新建好的空白项目是什么样的效果,点左下角绿色的三角:

这就是运行按钮,点击一下(项目新建路径用中文可能会有问题,遇到问题重新创建):

虽然什么都没有,但确实构建出了图形化界面,后续想办法如何修改代码让窗口变的丰富多彩。

6.代码解释

接下来看一下创建好的空白项目里都生成了哪些东西:

首先映入眼帘的是main.cpp这样一个文件,里面有个main函数。main函数有2个参数,第一个参数是命令行参数的个数,第二个参数是命令行参数都是什么内容。接下来创建了一个叫QApplication这样一个对象,并且把刚才的参数作为构造函数的参数传到这样的对象里面了。我们编写一个qt的图形化界面程序,一定需要有QApplication对象。再下来创建了一个Widget对象,并且调用了w.show。刚才在创建项目的时候,填写了生成的类名,当时我们填写的是Widget,于是qt creator就给我们生成了一个Widget这样的类,这个类有了后就生成了一个对象,并且调用show方法。这两行代码意思是,创建一个控件对象,并显示出来(.show()方法让控件显示出来,.hide()方法让控件隐藏。Widget的父类是QWidget,这些方法都是Qwidget提供的)。再然后还有个return a.exec(),exec表示让程序执行起来。之前linux中,也学过6个函数,exec系列,表示程序替换,把可执行文件中的代码和数据替换到当前进程中。当前qt的exec和linux中的exec没有任何关系,只是名字恰好是一样的。

现在把main函数看完了,继续看一下Widget类是什么样子的:

可以看到有个widget.h和widget.cpp,这两个文件包含了Widget类的声明和定义。先看一下头文件:

这个.h里面就是widget类的声明。1和2行是保证头文件只被包含一次,第10行看到Widget类继承自QWidget,这个QWidget就是我们创建项目时选择的父类。这个类是Qt SDK内置的,要想使用这个类,就需要包含对应的头文件,也就是第4行(Qt的设定:使用Qt中内置的类,包含的头文件的名字就是和类名一致的。当然也不是用到的所有的Qt的类都需要显示包含头文件,c++中头文件可能是间接包含的,比如引入了a.h,a.h里又包含了b.h,此时就相当于把a.h和b.h都包含了。后续写带代码的原则,一个Qt类,先拿过来用,如果直接能用,说明对应的头文件已经被间接包含过了,无需显示包含;如果这个类提示找不到定义啥的,手动的显示把对应头文件包一下就行了)。再下来在这个类里面有个Q_OBJECT,这是Qt内置的一个宏(宏本质上是文本替换),Q_OBJECT展开之后就会生成一大堆的代码。Qt中有一个非常核心的机制,"信号"和"槽",如果某个类想使用"信号和槽"就需要引入Q_OBJECT这个宏。在下面15和16是构造函数和析构函数,这有个parent参数,qt中引入了"对象树"机制(后面再详细介绍),相当于创建的Qt的对象,就可把这个对象给挂到对象树上,往树上挂的时候就需要指定当前对象的父节点。在下面有个Widget*ui,这个和formfile密切相关。再看看生成的.cpp文件中还有哪些东西:

首先看到两个头文件,第一个是创建项目生成的头文件,第二个是formfile被qmake生成的头文件。再下面是刚才的类构造函数具体的定义了,核心是给ui属性进行了创建实例并且有个setupui,其实6和8行是把formfile生成的文件(界面)和当前widget关联起来。再下面是个析构函数,就是把前面new的对象delete了。这里的关键要点就是form file,这是什么东西及会产生什么效果:

展开Forms里面有widget.ui,这就是所谓的form file,是个.ui后缀的文件。双击这个文件:

此时在qtcreator中它就切换到了设计这样的标签页并且进入到qtdesigner这样的界面。也就是当直接双击ui文件,此时Qt Creator就会调用Qt Designer来打开ui文件,此时看到的就是一个图形化的界面编辑器。来简单认识一下:

接下来点一下左侧边栏的编辑:

看到了ui文件本来的面貌。也就是ui文件内容本身是上面样子格式的内容(这个格式称为xml,它和html非常类似,使用成对的标签来表示数据),只不过这些内容会被Qt Designer当作图形化界面来去进行处理(扩展了解:xml这里的标签,有哪些标签,都表示什么含义,这是程序员自定义的,也就是此处看到的这些标签是qt开发者约定的。htm的标签是固定的,每个标签什么含义有专门标志委员会约定)。Qt中使用xml文件就是去描述程序的界面是啥样的,进一步的qmake会调相关的工具,依据这个xml文件生成一些C++代码,从而把完整的界面构造出来,这就是当前看到的ui文件。 接下来还有个很重要的东西需要我们重点关注一下,在我们生成的项目中还有个Empty.pro文件:

.pro文件是Qt项目的工程文件,也是qmake工具构建时的重要依据。第一行表示当前要引入的Qt的模块,后面学习到一些内容的时候可能会修改这里。第5行是个编译选项,期望当前编译器按照c++哪个版本来编译,可以通过这里来去指定。最关键的是18~27,这里描述了当前项目中,参与构建的文件都有什么(也就是编译器要编译哪些文件)。这个地方不需要手动修改,Qt Creator帮我们自动维护好。.pro类似于之前linux中学过的Makefile,指定好编译哪些等,qmake搭配.pro的作用和Makefile是类似的(Qt Creator把这个过程中编译的细节都封装好了,只需要点击运行按钮就直接编译)。

上面看到的.h .cpp .pro .ui都是源代码,如果编译运行Qt项目,构建过程中还会生成一些中间文件,我们来大概看一下:

这些是刚才的源代码文件。点上级目录:

看到和刚才项目目录并列的还有一个目录。在运行一次程序之后,就会在项目目录并列的地方多出来一个“build-xxxx”目录,这个目录里面就是该项目运行过程中,生成的一些临时文件。点进看看:

看到了Makefile,说明编译Qt程序还是会用到makefile,只不过这个makefile不需要手动写,而是qmake自动生成的。还看到有个ui_widget.h,这就是刚才widget.ui xml文件生成的.h文件。前面在widget.cpp中包含了这样一个文件,这个就是在这里被自动生成的。这个.h文件有什么呢:

这个文件里生成了一个Ui_Widget类,里面提供了setupUi方法(这个类其实就是widget.h中的Ui::Widget)。上面是Qt自动生成的代码,这个代码就会在setupUi这个方法中生成界面的具体细节,也看到widget.cpp中有使用这个方法。debug里面可以看到exe,这就是最终生成的可执行程序,如果直接运行效果和之前在Qt Creator中运行的效果一样。

7.Qt Hello World 程序

刚才创建项目并没有写任何代码,目前程序也可以跑起来看到空白的窗口,现在我们期望空白窗口上显示一个helloworld字符串。接下来我们就具体操作一下:首先还是打开Qt Creator(若创建新项目还是上述过程):

创建好项目后来进行helloworld的操作,我们期望在界面上显示helloworld的字符串,怎么显示呢?我们有两种方式进行完成:1.通过图形化的方式,在界面上创建一个控件,显示hello world。2.通过纯代码的方式,通过编写代码,在界面上创建控件,显示hello world。我们先通过图形化的方式来演示一下,打开widget.ui这个form file:

现在我们就调用了Qt designer这样的图形化的界面设计器去完成窗口界面内容的编辑。左侧是qt给我们提供的各种内置控件,我们想显示hellow world可以使用很多控件去完成,此处为我们使用一个最简单的,下面有个Label控件:

它所处的分类叫Display Widgets,顾名思义这个分类里的所有控件都是用来显示的。我们把这个控件拖到界面上一放:

此时界面上就出现了一个label控件。我们可以拖拽把label放大一些,方便后续编辑,然后双击可以进入编辑状态:

然后写入hello world:

此时修改就生效了,相当于界面存在了一个label控件。继续看右上边:

看到Qt Designer右上角通过树形的结构,显示出了当前界面上有哪些控件。这里看到有创建项目默认生成的QWidget控件,然后下面有个QLabel也就是label这样的控件。我们刚才的过程在main.cpp widget.cpp里面是没有变化的,变化的是widget.ui,点编辑可以看到:

刚才往界面拖拽了一个QLabel控件,此时ui文件的xml中就会多出这一段代码,这一段代表描述了当前界面上的label具体是什么样子。进一步的qmake就会在编译项目的时候,基于这个内容生成一段c++代码,通过这个c++代码构建出界面内容了(自动完成的)。下面运行一下:

看到窗口界面上出现了hello world。然后在右键目录在Exporer中显示,找到上级build目录:

这个ui_widget.h就是qmake根据widget.ui配置文件里的内容生成的头文件,在这个头文件里完成一些界面内容的初始化,完成创建label的过程。打开看一下:

看到setupui方法里有创建一个QLabel对象,然后进行一些设置,这样标签就被创建出来了。

接下来用纯代码方式实现一下helloworld,我们再创建一个新的项目:

把刚才的项目关了,直接创建右边栏会多出来个项目,后续操作容易分不清当前文件打开的是哪个项目(关闭后回到qtcreator的欢迎界面了,然后重新创建新的项目):

这里也生成了ui文件,我们不打算编辑它,而是通过代码的方式来完成。这里打开Widget.cpp:

这里包含了一个构造函数。一般我们通过代码来构造界面的时候,通常会把构造界面的代码放到Widget的构造函数中(选择的是继承QWidget父类,就放widget里面;选QMainWindow类,放到MainWindow构造函数里面)。构造函数是在main.cpp执行的:

一进入main函数先创建QApplication,然后创建Widget,这个创建的过程就执行了构造函数,也就执行了接下来要写的控件创建的过程。下面来编写,我们还是创建一个label对象,qt中叫QLable(通过new创建堆的方式创建):

此时编写代码时提示我们说QLabel找不到定义,因为Qt中每个类都有一个对应的同名的头文件,需要显示包含一下:

包含时发现有两个,这里解释一下:上古时期,Qt用的是qlabel.h这种风格的头文件;1998年之后,C++标准成立了,C++98规定包含头文件统一使用#include<cstiod>代替原有的#include<stdio.h>,因此Qt也做出了变化。这样就包好了:

再解释一下什么叫Label呢?label翻译过来是标签,准确来说是界面上一个用来显示内容的字符串控件,在界面上起到说明的效果,此处用QLabel来完成文字的显示。12行创建对象的时候还要注意一些细节:1.创建对象的时候可以在堆上创建,也可以直接在栈上创建:

qt中一般更推荐在堆上创建的方式。2.在我们new出对象的时候,构造函数这最好添一个参数this:

这个参数含义是给当前创建的label对象,指定一个父对象(前面提到过qt中有一个很重要的概念叫对象树,本质是一个分叉树,通过树形结构把当前界面上的控件对象组织到一起。树的特点是每个结点可以有n个子节点,只有一个父节点,指定这个对象的父节点也就知道它在树的哪个位置。这里是让创建的label对象的父节点是this,这个this是构造函数所对应的对象,这个对象是main函数里的Widget w,this指向的是main函数中创建的Widget对象,意思是我们创建的label对象它的父元素就是main中的Widget w对象)。

下面把label创建出来后再给label创建一个文本:

里面可以指定一个字符串。setText这是设置控件中,要显示的文本是啥。这里的形参类似是QString,这还有一个要交代的东西:qt诞生于1991年,那时c++还没有形成标准,因此那时c++更没有标准库这样的概念了。当时那个年代如何去表示一个字符串,可以使用c风格的字符串(\0结尾)、也可以使用c++的string。当年这些用起来都非常不好用,qt为了让自己的开发能变的顺畅,就自己发明了一套轮子,搞了一系列的基础类,来支持qt的开发,包括不限于:字符串 QString、动态数组QVector、链表 QList等。很多年之后,上述这些容器等内容已经打磨的很好了,形成了c++标准。很显然,这些已经引入的qt自己包装好的这些容器类也不可能删了,就只能和现有的标准库中的容器类共存了。因此我们开发qt代码的时候,如果需要用到上述容器类,可以使用标准库的容器,也可以使用qt自己搞的这一套。但是qt原生的api中,涉及到的接口用的都是qt自己的这一套容器。后续代码中,还会经常见到QString这样的一些东西,而很少见到std::string,QString和std::string之间也能很方便的相互转换。继续回到代码中:

在QString中也提供了c风格字符串作为参数的构造函数,不显示构造QString上述代码中c风格字符串也会隐式构造QString对象。QString对应的头文件,已经被很多qt内置的其它类给间接包含了,因此一般不需要显示包含QString头文件。下面运行一下:

此时看到界面中显示了helloworld字符串,只不过默认显示在左上角。

8.初识对象树

现在代码就编写完成了,但是有个细节问题:

这里是new的方式创建了一个对象,那么new完之后为什么没有delete呢?不delete不就出现内存泄漏了吗?但是上述12行的代码1,在qt中不会产生内存泄漏,label对象会在合适的时候被析构释放(虽然没有手动写delete,确实能释放)。关于怎么释放的奥秘在于this这里,之所以能够把对象释放调,主要是因为把这个对象挂到了对象树上。

下面说说,qt中搞了一个对象树,是个N叉树,把界面上的各种元素组织起来了。举个例子,比如有个这样的界面,这个界面最顶层窗口叫QWidget这样的对象,这个界面里还会包含其它一些元素:

这样的一个界面结构就会在qt内部通过一颗对象树来去进行表示。这个对象树大概样子是最上面根结点是QWidget对象,根下面包含了几个子结点:

这就是一个非常典型的树形结构,通过这个树形结构,就把界面上要显示的控件对象都组织起来了。为什么要用树形结构进行组织呢?使用对象树把这些内容组织起来,最主要的目的就是为了能够在合适的时机(窗口关闭/销毁),把这些对象统一进行释放。这里树上的这些对象,统一销毁是最好不过的,如果某个对象提前销毁,此时就会导致对应的控件就在界面上不存在了。因此前面第12行通过new的方式创建对象,也就是为了把这个对象的生命周期交给Qt的对象树来统一管理。试想一下如果这个对象是按照栈上的变量创建的,就可能会存在一些提前释放的问题。可以试一试在栈上创建变量,然后也指定父元素是this,再次运行一下:

当把对象改成在栈上创建,此时就可以看到运行起来的程序无法显示出hello world,此时label对象随着构造函数的结束就销毁了。所以用new的方式把对象生命周期交给qt的对象树更加合理,但是还是有人会担心不delete真的可以释放吗?下面改变一下代码看看自动释放的过程。

为了演示对象树帮我们自动释放对象的过程,我们需要再去把代码调整一下。我们之前是直接创建了一个QLable对象,这是qt里面内置的一个对象,这个对象析构的时候并没有一些打印提示之类的,因此我们看的并不是很清楚。为了更直观的看到这个对象被释放的过程,我们创建一个新的项目,然后自己定义一个QLable类,然后通过自定义的类看到自动释放对象的效果(先关闭之前的项目再创建新的项目):

再然后新创建文件那新建一个类出来:

然后就可以看到在qtcreater里面多出了mylable文件:

然后.h里面提示不认识QLable这个名字:

Qtcreator是帮我们生成了一些代码,但是没完全生成,头文件没给咱们自动包含,只能靠我们自己手动包含:

然后把构造函数也修改一下,加一个parent能够指定父元素:

然后切换到对应的.cpp修改一下(在qtcreator中,可以通过F4切换头文件和对应的.cpp文件):

这样就把构造函数修改完毕了。这里调用了父类构造函数,才能让我们自己类的对象加入到qt对象树中。创建自定义的类,最主要的目的是自定义一个析构函数,在析构函数中完成打印,方便我们看到最终的自动销毁对象的效果。继续完成析构函数:

声明还要有定义,可以光标放析构函数上,然后按atl+回车,然后点过去就跳转过去了并生成代码:

然后来编写,按照以往方式来写,然后创建对象运行一下:

看到helloworld正常显示,说明我们创建的Mylabel可以很好的继承QLabel的功能的,QLable可以干嘛Mylabel也就可以干嘛。目前程序没有什么输出:

下面关闭窗口触发析构来让对象释放:

发现有日志,说明析构函数是执行了。虽然没有手动delet,但是由于把MyLabel挂到了对象树上,此时窗口被销毁的时候,就会自动销毁对像树中的所有对象,MyLabel的析构是执行到了。但是预期的显示效果是"被销毁"三个中文,但是实际的显示效果出现了乱码。乱码问题出现的原因只有一个,那就是编码方式不匹配。

下面来说说乱码问题:在计算机中,一个汉字占几个字节?针对这个问题,只要回答出一个具体的数字,就一定是错的。因为要回答问题得先弄清楚前提条件,就是当前中文编码使用的是哪种方式(字符集)。计算机存的是二进制数字,怎么用二进制表示各种汉字或者符号呢?英文字母用ASCII表示,在ASCII中规定了每个字符都有一个对应的数字来表示。英文字母又没有多少,只是表示英文一个字节足够了。中文中一共6w左右汉字,那怎么表示汉字呢?思路和前面一致,仍然使用一个大表格,给每个汉字分配一个整数即可。对于计算机来说,6w多个符号的表格是小case,那具体这个表格什么样子?具体每个汉字都使用哪个数字表示?这个事情就不一定了(可能不同的组织有不同的表示方式),所以表示汉字的字符集其实是有很多种的,不同的字符集表示同一个汉字,使用的数字不相同。目前表示汉字字符集主要是2种方式:1.GBK(中国大陆使用多)使用2个字节表示一个汉字,像我们使用的Windows简体中文版默认的字符集就是GBK。2.UTF-8/utf8,这是个变长编码,表示一个符号,使用的字节数有变换,一般2~4,但是在utf8中一个汉字一般是3个字节,像Linux中默认就是utf8。现在说说为什么前面出现了乱码?就是编码方式不匹配。比如你字符串本身是utf8编码的,但是终端(控制台)是按照gbk的方式来解析显示的,此时就会出现乱码(拿着utf8这里的数值,去查询gbk的码表)。前面我们析构函数那里写了打印字符串:

这个字符串是包含在mylabel.cpp中的,这个字符串使用哪种方式编码的和当前mylabel.cpp文件的编码方式是一致的,怎么看这个文件的编码方式呢:

说明这个字符串是utf8方式的。QtCreator内置的终端是utf8的方式显示字符串吗?不好说,这个终端好像不能设置字符编码,但是出现了乱码肯定不是utf8。那怎么解决呢?无非是让编写和解析方式一样,但改终端的找不到,改文件也不好,因为主流方式还得是utf8。在Qt中有一个东西叫QString,是可以帮助我们自动处理编码方式的。不只是QString,也提供了专门用来打印日志的工具,也能自动处理编码方式,Qt中提供了一个qDebug工具,借助这个,就可以完成打印日志的过程,它能很好的处理字符编码,就不需要我们关注了。下面用一下:

QDebug是Qt中的类,但不会直接使用这个类,qDebug()是个宏,封装了QDebug对象,直接使用qDebug(),这个东西就可以当作cout来使用。下面运行关闭一下:

因此后续在qt中,如果想通过打印日志的方式输出一些调试信息,都优先使用qDebug。使用qDebug还有一个好处,打印的调式日志,是可以统一进行关闭的。输出的日志,是开发阶段调式程序的时候使用的,如果你的程序发布给用户,不希望用户看到这些日志可以快速通过qDebug的编译开关,来实现一键式的关闭。

9.补充

经过前面学习以及能用两种方式来显示helloworld,一个是纯代码的方式,一个是通过图形化界面的方式。其实qt中想完成helloworld可以基于很多控件来实现,前面是使用label完成的,label只是单纯的显示一个文本,没办法做更多的交互性质的工作,接下来换种方式再来实现一下helloworld。这里使用编辑框来完成helloworld,编辑框在qt中有两种,一种是单行编辑框,我们叫做QLineEdit;还有一个叫多行编辑框,叫做QTextEdit(我们当前用单行)。我们打开Forms文件,找到LineEdit:

然后把LineEdit拖到窗口上:

现在我们就有了一个LineEdit,接下来可以给LineEdit里面设置一个文本:

此时文本就有了。这里的helloworld文本可以通过右侧的属性编辑区来进行修改:

往下翻可以看到:

这些就是QLineEdit这个控件所对应的属性(含义后面说)。可以看到有一个属性是text,这个text就是编辑框里面包含的文本,可以在text里面修改文本的内容。下面运行一下:

而且文本框里面的内容也是可以编辑的,这就是通过编辑框的方式来完成helloworld的编写。

接下来再使用纯代码的方式来实现上述功能(再次新建项目,虽然有widget.ui但不去使用):切回到widget.cpp这里,在构造函数里面编写一下代码。我们创建一个QLineEdit*对象,并指定好父对象:

也就是当前在main函数里面创建的widget对象以它为父对象构建出了QLineEdit。然后仍然用setTex:

再次运行:

还可以通过按钮的方式来创建出helloworld,打开widget.ui,找到按钮:

这里面有个很重要的按钮叫PushButton,这是一个普通的按钮,一般都是使用这个按钮。下面把按钮拖到界面上,然后写入文本:

再次运行程序:

此时程序界面里面就有了一个按钮,按钮上显示的文本就是helloworld。按钮的使命就是点击以后会有效果,那怎么让按钮点击以后有效果呢?这里就涉及到了qt中的信号槽机制(后面说,这里演示使用方式),信号槽的本质就是给按钮的点击操作关联上一个处理函数,当用户点击的时候就会执行这个处理函数。我们打开widget.cpp,来给按钮添加点击处理操作。还是在构造函数这里,我们需要用到一个connect函数:

qt中的connect是QObject这个类提供的静态函数,这个函数作用是''连接信号''和''槽''(就是给点击关联一个处理函数)。这里的参数有四个,第一个参数是信号是谁发的,这里处理的是点击按钮的信号,所以是按钮发的信号,用ui->pushButton选中界面上的按钮:

通过ui->这样的写法可以访问到ui文件中创建的控件。我们前面创建了控件pushButton,创建这个控件时会带有一个属性叫objectName:

在QtDesigner中创建一个控件的时候,此时就会给这个控件分配一个objectName属性,这个属性的值要求是唯一的,不能和别人重复。这里会自动生成内容,也可以自己手动修改成别的,不管怎么样,qmake在预处理.ui文件的时候,就会根据这里的objectName生成对应的c++代码,c++代码中该QPushButton对象的变量名就是这里的objectName,这个变量就是ui属性中的成员变量。我们要处理按钮被点击的信号,第二个参数传的是一个函数指针:

这个东西是当我们点击按钮的时候,就会触发一个clicked这样的信号。我们期望这个对象能够关联到一个具体的槽函数上,先指定关联到哪个对象的槽函数:

这样就关联到Widget对象的一个槽函数。再后面指定一个具体的函数,也是传函数指针:

目前函数还没有定义。通过上述的代码,我们就完成了信号和槽之间的一个连接操作。这里点击ui->pushButton按钮(发出信号的主体)的时候就会自动触发clicked(发了个什么信号)这个信号,this是谁来处理这个信号,最后是具体怎么处理。接下来具体实现一下这个函数,先打开widget.h声明这里,在这里指定函数声明:

然后alt+回车添加定义:

再次运行一下:

再次点就不会变了。我们还可以调整一下实现一个切换的效果(直接->text是获取文本):

这个代码中通过ui->pushButton获取到ui文件界面上的按钮,之所以可以选中这个按钮是这个按钮有个属性叫objectName,由于这个属性叫pushButton,所以最终通过ui->pushButton来获取到这个对象。这个ui是widget.h里面有个Widget*:

ui指针是创建好项目后自动生成的,然后Widget里面的内容是qmake自动生成的。再次运行就会发现,每一次的点击文本内容都会发生变换。

看完程序执行效果以后来关注一下objectName是终止怎么变成ui里的属性的?还是右键.pro文件,在explorer中显示,然后进入上级目录,然后找到对应的build目录,里面有一个.h:

这就是根据widget.ui生成的文件。在当前生成的代码里,就会包含一个叫pushButton的成员:

ui_widget.h中自动生成的代码里就会包含一个pushButton的成员变量,这个变量的名字,就是更具objectName的值来确定的。在objectName中,设置成什么值,生成的变量名就叫什么名字,然后就可根据这个名字获取到对应的控件变量了。头文件中还可以看到:

又会生成一个类继承Ui_Widget,这个类是在Ui这样的命名空间里面,因此widget.h中引用就是这个类:

Ui_Widget里面的东西就可以通过ui来获取访问。

继续用纯代码的方式实现一下前面的效果,在widget.cpp中包含头文件QPushButton,然后创建一个按钮,记得指定父元素,然后设置一个文本:

写到这里界面上就以及有了一个按钮,按钮里有helloworld,下面运行一下:

接下来完成信号槽的连接,点一下按钮可以触发反应。继续用connect,connect里面指定谁发送的信号?是myButton。发送了一个clicked信号,this去处理这个信号,用handlerClick去处理这个信号:

接下来完成函数的声明和实现:

接下来和前面一样判断一下按钮里面的文本,但发现写myButton访问不到,因为myButton在构造函数中的一个局部域里,所以局部变量不能跨越函数去访问。因此把myButton变成Widget的成员变量就可以了:

此时就继续完善:

此时同样可以完成切换效果了:

接下来对比一下两个版本代码的差别:

对比发现,若是纯代码版本,意味着当前myButton按钮对象是自己new的,为了保证其它函数能访问到这个变量,就需要把按钮对象设定为Widget类的成员变量。若是图形化方式,此时的按钮对象不需要咱们自己new,new对象的操作已经是被Qt自动生成了,而且这个按钮对象,已经作为ui对象里的一个成员变量了,也无需作为Widget的成员。

10.Qt Creator的快捷键

11.使用帮助文档

打开帮助文档有三种方式,实际编程中使用哪种都可以。1.光标放到要查询的类名/方法名上, 直接按 F1。比如QPushButton这个类是干嘛的不知道,想去文档中查一下,光标选中按F1:

此时相当于把文档打开了,就可以看到QPushButton类的相关介绍了。2.Qt Creator左侧边栏中直接用鼠标单击 "帮助" 按钮:

看到把文档各个方面的内容分门别类以目录形式放好了。3.找到 Qt Creator 的安装路径,在 "bin" 文件夹下找到 assistant.exe,双击打开:

12.认识qt坐标系

在qt中使用的坐标系就是数学里常见的平面直角坐标系,但和数学中的有些区别:

一般数学上的称为右手坐标系,计算机中的称为左手坐标系。计算机中的坐标系原点(0,0)就是屏幕左上角或者窗口左上角。给qt的某个控件设置位置,就需要指定坐标,对于这个控件来说,坐标系原点就是相对于父窗口或控件的。比如这是显示器的屏幕,屏幕里有qt的窗口QWidget,QWidget里面有个按钮QPushButton:

QPushButton的父元素/父控件/父窗口就是QWidget,QWidget没有父元素(NULL)就相当于父元素就是整个显示器的桌面了。

下面用代码来演示一下,创建好项目,然后在widget.cpp里面通过代码创建一个QPushButton:

当前的button不去设置默认就出现在左上角:

此时默认情况下按钮就是在(0,0)位置,就是左上角。可以使用move函数来设置按钮的位置:

参数一个是横坐标,一个是纵坐标。谈到坐标,坐标背后的单位是像素。显示器本质上就是一大堆可以发光的小亮点/小灯泡来构成的,比如见过显示器分辨率是1920*1080这样的文字,这说的是水平方向有1920个像素(亮点),垂直方向有1080个像素。显示器亮点数量越多,画面就越好。下面运行程序看一下:

也可以给Widget本身移到位置: