1.QT简介

1.1 跨平台图形界面引擎

1.2 优点

  • 跨平台
  • 接口简单,容易上手
  • 一定程度上简化了内存回收

1.3 版本问题

  • 商业版
  • 开源版(Qt 5.9)

1.4 成功案例

  • linux 桌面环境 KDE
  • 谷歌地图
  • VLC 多媒体播放器

2.创建第一个 Qt 程序

2.1 点击创建项目后,选择项目的路径和名称

2.2 名称不能有中文和空格

2.3 路径不能有中文名称

2.4 默认创建窗口类:myWidget,基类有三种选择:QWidget,QMainWidget,QDialog

2.5 main 函数

  • QApplication a 应用程序对象,有且仅有一个
  • myWidget w; 实例化窗口对象
  • w.show(); 调用show函数,显示函数
  • return a.exec(); 让应用程序对象进入消息循环机制中,代码阻塞到当前行
// main.cpp

#include "mywidget.h"
#include <QApplication> //包含一个应用程序类的头文件


// mian 函数入口    argc命令行变量的数量  argv命令行变量的数组
int main(int argc, char *argv[])
{
    // a 应用程序对象,在qt中,应用程序对象有且仅有一个
    QApplication a(argc, argv);
    //窗口对象 myWidget父类 -> QWidget
    myWidget w;
    //窗口对象默认不会显示,必须调用show方法展示窗口
    w.show();

    //让应用程序对象进入消息循环
    //让代码阻塞到这行
    return a.exec();
}

2.6 pro工程文件解释

// Qt 包含的模块
QT       += core gui

// 大于 4 版本以上,包含 widget 模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

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

DEFINES += QT_DEPRECATED_WARNINGS

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

// 头文件
HEADERS += \
        mywidget.h

2.7 .h 文件解释

#ifndef MYWIDGET_H
#define MYWIDGET_H

// 包含头文件 Qwidget    窗口类
#include <QWidget>

// 类 myWidget 继承父类 QWidget
class myWidget : public QWidget
{
    // Q_OBJECT宏,允许类中使用信号和槽的机制
    Q_OBJECT

public:
    // 构造函数(有参构造)
    myWidget(QWidget *parent = 0);
    // 析构函数
    ~myWidget();
};

#endif // MYWIDGET_H

3. 按钮控件常见API

3.1 创建 QPushButton * btn=new QPushButton;
3.2 设置父亲 btn->setParent(this);
3.3 设置文本 btn->setText("第一个按钮");
3.4 设置位置 btn2->move(宽,高);
3.5 重新指定窗口大小 resize(600,400);
3.6 设置窗口标题 setWindowTitle("第一个窗口");
3.7 设置窗口固定大小 setFixedSize(600,400);

// mywidget.cpp

#include "mywidget.h"
#include <QPushButton> // 按钮控件的头文件

// 命名规范
// 类名 首字母大写 ,单词与单词之间首字母大写
// 函数名 变量名称 首字母小写

// 快捷键
// 注释:ctrl + /
// 运行:ctrl + r
// 编译:ctrl + b
// 字体缩放 ctrl + 鼠标滚轮
// 查找 ctrl + f
// 整行移动 ctrl + shift + ↑ 或者 ↓
// 帮助文档 F1
// 自动对齐 ctrl + i
// 同名之间的 .h 和 .cpp 切换


// 帮助文档 第一种方式 F1    第二种方式 左侧按钮
myWidget::myWidget(QWidget *parent)
    : QWidget(parent)
{
    // 创建一个按钮
    QPushButton * btn=new QPushButton;
    //  btn->show();// show 以顶层方式弹出控件
    // 让btn对象 依赖在 myWidget 窗口中
    btn->setParent(this);

    // 显示文本
    btn->setText("第一个按钮");

    //创建第二个按钮
    QPushButton * btn2=new QPushButton("第二个按钮",this);

    // 移动btn2窗口
    btn2->move(100,100);

    //重置窗口大小
    resize(600,400);

    //设置固定的窗口大小
    setFixedSize(600,400);
    //设置窗口标题
    setWindowTitle("第一个窗口");


}

myWidget::~myWidget()
{

}

第一天笔记-202110181072.png

4. 对象数

4.1 当创建的对象在堆区时候,如果指定的父亲是 QObject 派生下来的类或者 QObject 的子类派生下来的类,可以不用管理释放的操作,将对象会放入对象树中。

  • QWidget是能够在屏幕上显示的一切组件的父类
    • QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。

4.2 一定程度上简化了内存回收机制。

  • 当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。

  • 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。
    第一天笔记-2021101815158.png

5. qt 中的坐标系

以左上角为原点(0,0),X向右增加,Y向下增加。

第一天笔记-20211018151848.png

6. 信号与槽

第一天笔记-20211018154314.png
  • 6.1 连接函数 connect

  • 6.2 参数

    • 6.2.1 参数1 信号发送者
    • 6.2.2 参数2 发送的信号
    • 6.2.3 参数3 信号接受着
    • 6.2.4 参数4 处理的槽函数
  • 6.3 松散耦合

  • 6.4 实现 :点击按钮,关闭窗口的案例

第一天笔记-20211018154751.png
    //创建第二个按钮
    QPushButton * btn2=new QPushButton("第二个按钮",this);

    // 移动btn2窗口
    btn2->move(100,100);

    // 需求: 点击myBtn 关闭窗口
    //参数1 信号的发送者 参数2 发送的信号(函数的地址) 参数3 函数的接受者  参数4 处理的槽
    connect(btn2,&QPushButton::clicked,this,&QWidget::close);```

7. 自定义的信号与槽

  • 7.1 自定义信号
    • 7.1.1 写到 signals 下
    • 7.1.2 返回值为void
    • 7.1.3 需要声明,不需要实现
    • 7.1.4 可以有参数,可以重载
  • 7.2 自定义槽函数
    • 7.2.1 写到 public slot 或者 public 或者全局函数
    • 7.2.2 返回为void
    • 7.2.3 需要声明,需要实现
    • 7.2.4 可以有参数,可以重载
  • 7.3 触发自定义信号
    • 7.3.1 emit 自定义信号
  • 7.3 案例:下课后,老师发出饿了信号,学生响应,并请老师吃饭
第一阶段笔记-20211022192249.png
第一阶段笔记-20211022192323.png
第一阶段笔记-20211022193138.png
第一阶段笔记-20211022193138.png
第一阶段笔记-20211022195413.png
第一天笔记-20211018213946.png

8. 自定义信号和槽出现重载

  • 需要利用函数指针明确指向函数的地址
    //  链接带参数的槽
    // 指针 -》 地址
    // 函数指针 -》 函数地址
    void(Teacher:: *teacherSign)(QString)=&Teacher::hungry;
    void(Student:: *studentSlot)(QString)=&Student::treat;
    connect(zt,teacherSign,st,studentSlot);
    
第一阶段笔记-20211022192855.png
  • QString 类型转换为 char * 类型:解决打印出的值带有双引号
    • 通过 toUtf8() 转换成 QByteArray 类型
    • 然后通过 .data() 装换成 char * 类型
      第一天笔记-20211019205851.png
void Student::treat(QString foodName){
    qDebug()<<"eat:"<<foodName;
    // QString 转 char *:先转成 QByteArray(.toUtf8())再转成char *(.data())
    qDebug()<<"eat:"<<foodName.toUtf8().data();
}
  • 信号可以链接信号
    //无参信号与槽链接
    void(Teacher:: *teacherSign2)(void)=&Teacher::hungry;
    void(Student:: *studentSlot2)(void)=&Student::treat;
    connect(zt,teacherSign2,st,studentSlot2);

    //信号链接信号  按钮信号链接老师饿了的信号,直接触发
    connect(btn,&QPushButton::clicked,zt,teacherSign2);
第一天笔记-20211019212236.png
  • 断开信号 disconnect
第一天笔记-20211019212324.png
第一天笔记-20211019212445.png

8. 拓展:

  • 1.信号可以链接信号
    • 当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
  • 2.一个信号可以链接多个槽函数
    • 如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
  • 3.多个信号可以同时链接同一个槽函数
    • 只要任意一个信号发出,这个槽就会被调用。
  • 4.信号与槽函数的参数 必须类型一一对应
  • 5.信号与槽的参数的个数:信号的个数可以多于槽函数,但类型需要一一对应

9. qt4版本的信号与槽

connect(zt,SIGNAL(hungry(),st,SLOT(treat());
connect(信号的发送者,发送的信号SIGNAL(信号),信号的接受者,槽函数SLOT(槽函数))

优点:参数直观
缺点:类型不做检测
qt5以上版本支持QT4版本的写法

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

10. lambda 表达式函数、

函数:[](){};
函数调用:[](){}();

[函数对象参数](操作符重载函数参数){函数体}
[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}

    1. 函数对象参数
      [],标识一个 Lambda 的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
    • 空。没有使用任何函数对象参数。
    • =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
    • &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
    • this。函数体内可以使用Lambda所在类中的成员变量。
    • a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
    • &a。将a按引用进行传递。
    1. 操作符重载函数参数
      标识重载的 () 操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
    1. 可修改标识符
      mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
      第一天笔记-2021102011112.png
    1. 函数返回值
      ->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
      第一天笔记-202110201139.png
    1. 函数体
      {},标识函数的实现,这部分不能省略,但函数体可以为空。
    //利用lambda表达式 实现点击按钮关闭窗口
    QPushButton * btn2 = new QPushButton;
    btn2->setParent(this);
    btn2->setText("关闭");
    btn2->move(100,0);
    // connect(btn2,&QPushButton::clicked,this,&Widget::close);
    //当第三个参数是 this 时,可以省略不写
    connect(btn2,&QPushButton::clicked,this,[=](){
        this->close();
    });