QT——day01

一、项目创建以及默认生成的main文件和Pro文件

1.项目创建

1.1 项目名称和路径

  在QT中,创建的项目名称和路径不能有空格和中文。可以加下划线。
在这里插入图片描述

1.2 创建项目时的基类

  在创建项目的时候,有三个基类可以选择,分别是QWidget,QMainWindows,QDialog。这三种的关系是什么?

  QWidget 是 QMainWindows,QDialog 的父类,也就是QMainWindows,QDialog 继承了QWidget。
在这里插入图片描述
  (1)QWidget :现在我们可以简单地将 QWidget 看为一个空窗口,就是里面什么都没有(没有各种菜单栏工具栏)。

在这里插入图片描述
  补充:
在这里插入图片描述

  (2)QMainWindows:继承了QWidget,子类是可以有自己的特性,那么现在可以简单地认为 QMainWindows 就是有各种工具栏,菜单栏的窗口。

  MainWindow的结构分为五个部分:菜单栏(Menu Bar)、工具栏(Toolbars)、停靠窗口(Dock Widgets)、状态栏(Status Bar)和中央窗口(Central Widget)
在这里插入图片描述
  (3)Qdialog:是对话框,可以简单地理解为我们平时所用的一些网站或者软件弹出来的提示,比如说输入的时候密码用户错误等等。
在这里插入图片描述

1.3 创建QWidget 类

  现在就先创建一个最简单的 QWidget 类:
在这里插入图片描述

1.4 main 文件简单介绍

在这里插入图片描述

  (1)从上面图片可以看出,程序把所有的命令行变量和命令行变量数据(argc 和 argv)交给应用程序对象(a)管理了。

  (2)本来代码运行完了就会一闪而过,但是有了下面这句代码就不会。
在这里插入图片描述

1.5 Pro 文件

  .pro 文件也称为项目工程文件,一般不会在这个文件写东西。是要通过 .pro 文件生成其他的文件的。
在这里插入图片描述
  (1).pro 文件的内容

QT       += core gui      //Qt包含的模块,这里有开发所要用的很多类

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets    //大于4版本  加入 widgets   模块 

TARGET = 01_QtFirst   //生成.exe程序的名称
TEMPLATE = app        //模板 应用程序模板


SOURCES += main.cpp\   //源文件
        mywidget.cpp
	

HEADERS  += mywidget.h  //头文件

1.6 .h 文件

在这里插入图片描述
(1)Q_OBJECT 这一个宏,加了这个宏,就支持信号和槽。
在这里插入图片描述

2.项目的文件

  当我创建了一个项目之后,一般会有四个文件:项目的工程文件.pro 文件;项目的头文件.h;程序的入口 main 函数文件;还有项目要实现的东西,比如说要做一个窗口的.cpp 文件,这个文件通常是要做的窗口的类的构造函数,我们会在这个构造函数里面增加各种部件。然后在 main 函数中调用,创建窗口类的一个对象,这样就能对窗口进行实例化,然后展示我们做的窗口。
在这里插入图片描述

(1)mywidget.cpp
  通常我们所要增加的部件就是在这个构造函数里面加。
在这里插入图片描述
(2)main.cpp
在这里插入图片描述

二、命名规范和常用快捷键

1.命名规范

在这里插入图片描述

2.常用快捷键

在这里插入图片描述

三、QPushButton 按钮

  在使用前包含头文件:
在这里插入图片描述

1.创建按钮第一种方法

  这一种方法调用的是创建按钮的默认构造函数。如果想要进行其他的操作,那么需要去调用其他的函数。

  比如说显示按钮,设置按钮的文字。尤其要注意,创建了一个按钮,如果不做依赖,那么按钮就是一个独立的对象。这是什么意思呢?就是按钮会另开一个新的窗口,而不是放在当前的窗口上。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.创建按钮第二种方法

在这里插入图片描述

3.按钮移动 move

  防止按钮覆盖:

 btn2->move(50,50);

在这里插入图片描述

4.重置窗口大小 resize

在这里插入图片描述
  只运行一行代码的效果:
在这里插入图片描述
  所以要重置窗口的大小:使用resize方法
在这里插入图片描述在这里插入图片描述

5.设置窗口标题 setWindowTitle

在这里插入图片描述

在这里插入图片描述

6.设置窗口固定大小 setFixedSize

// 设置窗口固定大小
    setFixedSize(600,400);

在这里插入图片描述

四、Qt中的对象模型(对象树)

  在前面的步骤中,我使用 new 创建了两个按钮,但是并没有写释放的函数。
在这里插入图片描述
  其实这两个对象在程序执行完了之后都会被释放。

  在Qt中一定程度上简化了内存回收机制,当创建的对象 指定的父亲是由QObject或者Object派生的类时候 ,这个对象被加载到对象树上。当窗口关闭掉时候,树上的对象也都会被释放掉。

  这个就有点株连九族的味道了,如果一个对象指定了父亲是 QObject或者Object派生的类(比如QWidget等等),父亲被干掉了,就会连累孩子列表中的对象。
在这里插入图片描述

  (1)在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。

  当父对象析构的时候,这个列表中的所有对象也会被析构。也就是爸爸被干掉了,儿子们也会被干掉(注意,这里的父对象并不是继承意义上的父类!)

  (2)QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。

  (3)我们也可以自己删除子对象,它们会自动从其父对象列表中删除。

五、Qt坐标系

  以左上角为原点(0,0),X向右增加,Y向下增加。
在这里插入图片描述

六、信号槽

1.信号槽简介

  信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式

  当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。

  这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号

  也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

  connect 函数的使用方法:
在这里插入图片描述

2.系统自带的信号和槽

  上面我们已经学习了按钮的创建,但是还没有体现出按钮的功能,按钮最大的功能也就是点击后触发一些事情,比如我们点击按钮,就把当前的窗口给关闭掉,那么在Qt中,这样的功能如何实现呢?

	QPushButton * quitBtn = new QPushButton("关闭窗口",this);
	
  connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);

3.我个人的理解

3.1

  Qt中的信号和槽机制,其实是为了将两个没有联系的对象联系起来,从而满足一些需求。

  比如说,客户有这样的需求:界面想要有一个按钮,点击按钮,界面窗口就能关闭。但是问题来了,按钮和界面窗口是两个独立的对象,彼此发生的动作是不知道的呀。意思就是说,界面怎么知道按钮被点击了;按钮点击了之后,界面窗口为什么会知道去关闭,而不是放大收缩?等等。

  所以必须要将这两个对象联系起来,并且按钮被点击了之后,能够通知界面窗口;收到了通知之后界面窗口要去执行规定的操作。从这里也可以推出,两个对象联系起来有四要素:

(1)两个对象的标识符;

(2)特定的事件发生了才通知另一个对象,而不是随便一个事件发生了就通知。所以要有指定的事件。其实这样解释不对,这是观察者模式,观察者观察到被观察者发生了感兴趣的事情,就去执行对应的动作,这样才是对的。

(3)执行指定操作。

3.2

 connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);

  这里发出的信号和处理信号的函数为什么是:

&QPushButton::clicked

&MyWidget::close

  而不是用实例后的对象去调用呢?也就是为什么不是这样写呢?这是系统自带的信号,按钮时属于系统中某个类的对象,如果系统有满足需求的信号,使用系统自带的就行,这样很方便,也便于别人阅读代码。毕竟都是公有的嘛。

  像下面这种写法,可能是要自定义信号和处理函数。

 connect(quitBtn,quitBtn->clicked,this,this->close);

4.自定义信号和槽

  下面模拟一个案例,下课后,老师发出饿了的信号,学生接收信号后请老师吃饭。

  这个案例还需要老师和学生这两个类,所以我们要加在项目里面,项目里面快速添加类的方法:就是在项目名称文件,也就是最顶级的文件(与项目名称同名)那里,点击鼠标右键,然后添加新文件。就可以快速添加老师和学生类,并且会自动生成相应的头文件和源文件。

在这里插入图片描述

4.1 自定义信号

  信号其实就是一个函数,自定义信号就是自定义函数,不过有些要注意的地方:
在这里插入图片描述
  (1)在这个案例里面,信号是由老师发出来的,信号是一个函数。函数在使用前,也是要声明;函数的声明一般是在头文件中。所以我们将饿了这个信号函数的声明写在 Teacher.h 头文件里面:
在这里插入图片描述

4.2 自定义槽

  槽就是处理信号的函数:
在这里插入图片描述
  (1)将处理信号的函数的声明写在 Student.h 头文件里面:
在这里插入图片描述
  (2)将处理信号函数的实现,写在 Student.cpp 文件中:
在这里插入图片描述

4.3 包含自定义信号和槽的头文件

  在项目中窗口头文件中,要使用学生类和老师类记得包含他们的头文件,并且我们通常会将老师对象和学生对象作为项目的属性,包含在窗口类中。

在这里插入图片描述

4.4 需要触发信号,发出信号

  在教师类中,定义了信号函数,也将教师和学生用 connect 联系了起来,那么学生能捕捉到老师发出的信号了吗?还不能,因为信号还没有被触发,所以说在定义的信号和槽中,要记得写触发信号的函数,也就是将信号发出的函数。
在这里插入图片描述
在这里插入图片描述

4.5 自定义信号和槽进行重载

(1)connect 函数的参数数据类型

  在说自定义信号和槽重载之前,再来看看 connect 函数的参数列表都是什么:
在这里插入图片描述
  可以知道 connect 函数的参数四要素是:信号的发送者,信号的函数,信号的接受者,处理信号的函数。但这四要素是什么数据类型呢?Qt里面嵌套的是 C/C++ 的代码,C/C++ 里面参数传递的方法无非就是三种:值传递,指针传递,引用传递。值传递是不会用的一般情况下,所以这里无非就是使用了指针或者引用,但是引用的本质也是指针,所以 connect 函数的四个参数都是指针。

  1)第一,三个参数:指向信号发送者和信号接收者的指针

  2)第二,四个参数:指向信号函数的函数和信号处理函数的函数指针

	this->zt = new Teacher(this);
    this->st = new Student(this);
	connect(zt,&Teacher::hungry,st,&Student::treat);

  看上面这段代码中的 &Teacher::hungry&Student::treat这两个是什么意思?这是connect 函数的第二、四个参数,是函数的指针。那么为什么要这样写呢?

  hungry ,treat 是函数名,函数名本身就是指针。但是类名并不是指针,hungry 和 treat 分别是 Teacher 和 Student 类中的成员函数。所以要先找到 Teacher 和 Student 类,就是对这两个类进行取地址(&),再加上函数的作用域符(::)。

(2)自定义信号和槽进行重载会出现的问题

  1)重载信号函数

  我们使用的案例仍然是下课了,老师发出饿了的信号,但是这一次老师说要吃“戳毛煎鸡蛋”,也就是发出了新的信号。那么我们需要重新生命信号函数,这样是可以的,但这用的是C++,可以对原有的信号函数进行重载。

  重载信号函数时,只需要在函数的参数列表加上“戳毛煎鸡蛋”对应的数据类型,是QString。信号函数是在 Teacher 类中的。
在这里插入图片描述
  2)重载信号处理函数

  有了新的信号,那么信号接收者要有新信号的处理函数,这里我们可以新定义一个处理函数,也可以进行重载,现在选择重载。
在这里插入图片描述

  3)重载函数出现的问题

  将新信号和新信号处理函数联系起来,问题就来了。从上面的图片可以知道,现在 Teacher 和 Student 类中分别有两个信号函数和两个信号处理函数。在调用 connect 函数的时候,怎么准确地将信号函数和信号处理函数传给 connect 函数,因为现有一个类有两个。

  比如说,我现在想要传新的信号和新的处理函数,像下面的写法,是无法准确定位到的,会出现二义性,也就是编译器不知道你传的是新的还是旧的函数的指针。

connect(zt,&Teacher::hungry,st,&Student::treat);
connect(zt,&Teacher::hungry,st,&Student::treat);

  4)解决问题

  我们知道第二、四个参数是函数指针。现在要解决的问题就是准确定位到新或者旧函数。那么我可以想办法将新或者旧函数的地址取出来,然后定义一个函数指针来接收,接着就将函数指针传给 connect 函数,问题就解决了。

  所以解决的步骤就有两个:1、取出目标函数的地址,放到函数指针中;2、将函数指针传给 connect 函数。

  现在我要使用的是新的信号和新信号处理函数:

 void hungry(QString foodName);
 void treat(QString foodName);

  所以要先声明两个两个函数指针,函数指针的声明方法:函数的返回值 + (*函数指针名) + (函数的参数列表)

 	void(Teacher:: *teacherSingnal)(QString) = &Teacher::hungry;
    void(Student:: *studentSlot)(QString)    = &Student::treat;

  上面的代码又给了我惊喜,为什么 &Teacher::hungry 不用再上 QString 的参数呢?像&Teacher::hungry(QString) 这样写呢?

  首先这样写编译器不知道是什么意思,没有这种语法。还是那句话,hungry 本身就是指针了,那么这样写怎么知道是哪个函数?void(Teacher:: *teacherSingnal)(QString) 这句代码就已经做了限定,告诉编译器要去返回值为 void 的 参数列表为 QString 的函数的,相当于就做了优化,等号右边就不用加什么了。

  Teacher:: *teacherSingnalStudent:: *studentSlot 这两个指针要记得加上作用域,Teacher 和 Student,不然不知道是哪个类中的函数指针。

5.信号和槽拓展

  在前面的例子里,当下课了这个函数被调用了,老师类中的饿了信号才会被触发,也就是被发出去。这是利用调用函数来触发信号,那么我们现在是来做界面的,就希望能够用其他的部件来触发信号。比如说有一个下课的按钮,当下课的按钮被点击了,就触发了信号,就能够将信号发出去,信号接收者能够接收到,并且能够去处理信号。这个怎么实现呢?
在这里插入图片描述

  按钮被点击,然后信号被触发,接着信号接收者能够接收到信号,并能够处理信号。 这个需求有四个对象,按钮,信号函数,信号接收者,信号处理函数。

  (1)首先要将老师和学生对象联系起来,这是不变的

 	this->zt = new Teacher(this);
    this->st = new Student(this);
	QPushButton * btn = new QPushButton("点击下课",this);
    resize(600,400);
    
    void(Teacher:: *teacherSingnal_1) = &Teacher::hungry;
    void(Student:: *studentSlot_1)    = &Student::treat;
    
    connect(zt,teacherSingnal_1,st,studentSlot_1);

  (2)接着就是按钮去触发信号,需要做一个动作,将信号触发,将信号发出去。这里将点击按钮和信号触发联系在一起,也就是点击了按钮,信号就会发出去,也是使用 connect 函数。所以按钮要和教师对象联系起来。

 connect(btn,&QPushButton::clicked,zt,teacherSingnal_1);

  其实就是按钮发送点击下课的信号给教师,教师收到了信号之后;就将饿了的信号发出去,学生接收到了饿了的信号,处理信号,请老师吃饭。

  这里需要注意的点就是,老师饿了的信号,要收到了按钮发过来的信号才能发出去,之前的是调用一个函数来将饿了的信号发出去。

在这里插入图片描述
  (3)总结
   1)一个信号可以和多个槽相连:

  如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。

  2)多个信号可以连接到一个槽:

  只要任意一个信号发出,这个槽就会被调用。

   3)一个信号可以连接到另外的一个信号:

  当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。

   4)槽可以被取消链接:

  这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。

6.Qt4的信号和槽的写法

connect(zt,SIGNAL(hungry(QString)),st,SLOT(treat(QString)));

  这里使用了SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。注意到connect()函数的 signal 和 slot 都是接受字符串,一旦出现连接不成功的情况,Qt4是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。

  Qt5在语法上完全兼容Qt4,而反之是不可以的。

七、Lambda 表达式

  C++11中的Lambda表达式用于 定义并创建匿名的函数对象,以简化编程工作。

  首先看一下Lambda表达式的基本构成:

[capture](parameters) mutable ->return-type
{
statement
}
[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值