QT第一天

本文详细介绍了QT开发库的特性,强调其跨平台优势,简化内存回收。讲解了创建Qt项目的步骤,包括.pro文件、main.cpp和Widget类的使用。详细阐述了信号和槽机制,展示了如何通过connect函数实现信号与槽的连接,并通过案例说明自定义信号和槽的用法。还探讨了Lambda表达式在QT中的应用,以及在实际项目中的练手案例,帮助读者深入理解QT开发。
摘要由CSDN通过智能技术生成

一、介绍

1.1 介绍

QT是一套应用程序开发库,但与MFC不同,QT是跨平台的开发类库

跨平台意味着只需要编写一次程序,在不同平台无需改动或只需少许改动编译,就可以形成在不同平台上运行的版本。

1.2 优点

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

二、注意事项

2.1名称跟创建的路径不能含中文!!!

2.2如果不编译,会显示红色

在这里插入图片描述

2.3 窗口类

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

默认创建有窗口类myWidget,基类有三种选择:QWidget(类似于空窗口),QMainWindow以及QDialog三个

QWidget 是所有用户界面元素的基类,窗口和控件都是直接或间接继承自 QWidgetQMainWindowQWidgetQDialog 三个类就是用来创建窗口的,可以直接使用也可以继承后再使用。
QMainWindow 窗口可以包含菜单栏、工具栏、状态栏、标题栏等,是最常见的窗口形式,可以作为GUI程序的主窗口。
QDialog 是对话框窗口的基类。对话框主要用来执行短期任务,或与用户进行互动,它可以是模态的也可以是非模态的。QDialog 没有菜单栏、工具栏、状态栏等。

三、创建Qt项目

3.1 pro文件

.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件

3.2 一个简单的Qt程序

程序介绍:

在这里插入图片描述

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

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

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

3.3 .pro文件解释

在这里插入图片描述

QT       += core gui   Qt包含的模块

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

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


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

HEADERS  += mywidget.h    //头文件

3.4 weidget.h 头文件

#ifndef WIDGET_H  //防止重复包含(防止重命名,防止头文件重复编译)
#define WIDGET_H

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

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

public:
    Widget(QWidget *parent = nullptr);  //有参构造函数,默认值
    ~Widget();  //析构函数
};
#endif // WIDGET_H

3.5 命名规范

类名 首字母大写,单词和单词之间首字母大写

函数名和变量名 首字母可以小写,单词和单词之间首字母大写

3.6 快捷键

注释 ctrl + /

运行 ctrl + r

编译 ctrl + b

字体缩放 ctrl + 鼠标滚轮

查找功能 ctrl + f

整行移动 ctrl + shift + ↑ 或者 ↓

帮助文档 F1

自动对齐 ctrl + i

同名之间的.h和.cpp切换 F4

3.7 main.cpp

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

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

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

3.8 按钮控件常用API

示范如何使用帮助文档:

QPushButton

在这里插入图片描述

头文件:要包含 #include<QpushButton>

qmake :所属的模块widgets

Inherits :继承的父类QAbstractButton

Inherited By:子类QCommandLinkButton

再点击QabstractButton会发现又继承QWidget

在这里插入图片描述

3.9 widget.cpp

#include "widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //①、创建一个按钮
    QPushButton * btn = new QPushButton;
    //btn->show(); //show以顶层方式弹出窗口控件
    //让btn对象,依赖在widget 窗口中
    btn->setParent(this);

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

    //②、创建第二个按钮 按照控件的大小创建窗口
    QPushButton * btn2 = new QPushButton("第二个按钮",this);

    //移动btn2按钮
    btn->move(100,100);

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

Widget::~Widget()
{
}


常用的:

  1. 创建 QPushButton * btn = new QPushButton

  2. 设置父亲 setParent(this)

  3. 设置文本 setText(“文字”)

  4. 设置位置 move(宽,高)

  5. 重新指定窗口大小 resize

  6. 设置窗口标题 setWindowTitle

  7. 设置窗口固定大小 setFixedSize

3.10 对象模型(对象树)

在这里插入图片描述

  • 当创建的对象在堆区时候,如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类,可以不用管理释放的操作,将对象会放入到对象树中。
  • 一定程度上简化了内存回收机制

3.11 Qt窗口坐标系

坐标体系:

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

在这里插入图片描述

四、信号和槽的机制

4.1 连接函数 connect

在这里插入图片描述

connect(信号的发送者, 发送的具体信号,信号的接受者,信号的处理(槽))
注:如果信号的接受者是 this ,那么可以省略

信号槽的优点,松散耦合,信号发送端和接收端本身是没有关联的,通过connect连接,将两端耦合在一起

在这里插入图片描述

4.2 参数

  1. 参数1 信号的发送者
  2. 参数2 发送的信号(函数的地址)
  3. 参数3 信号的接受者
  4. 参数4 处理的槽函数

4.3 松散耦合

4.4 实现 点击按钮 关闭窗口的案例

connect(myBtn,&QPushButton::clicked,this,&QWidget::close);

五、自定义信号和槽

在这里插入图片描述

5.1 自定义信号

  1. 返回void
  2. 需要声明,不需要实现
  3. 可以有参数
  4. 写到signals 下

5.2 自定义槽函数

  1. 返回void
  2. 需要声明,也需要实现
  3. 可以有参数,可以重载
  4. 写到 public slot 下或者 public 或者 全局函数

5.3 触发自定义的信号

emit 自定义信号

5.4 案例

​ 下课后,老师触发了信号,学生响应信号,请客吃饭

student.h

#ifndef STUDENT_H
#define STUDENT_H

#include <QObject>



class Student : public QObject
{
    Q_OBJECT
public:
    explicit Student(QObject *parent = nullptr);

signals:

public slots:
    //槽函数
    //早起Qt版本 必须写到public slots 高级版本可以写到public或者全局下
    //返回值 void ,需要声明,也需要实现
    //可以有参数,可以发生重载
    void treat();
};

#endif // STUDENT_H

teacher.h

#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>

class Teacher : public QObject
{
    Q_OBJECT
public:
    explicit Teacher(QObject *parent = nullptr);

signals:
    //自定义信号 写到signal下
    //返回值是void 只需要声明,不需要实现
    //可以有参数 ,可以重载
    void hungry();


};

#endif // TEACHER_H

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "student.h"
#include "teacher.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    Teacher * zt;
    Student * st;

    void classIsOver();

};
#endif // WIDGET_H

main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

student.cpp

#include "student.h"
#include <QDebug>


Student::Student(QObject *parent) : QObject(parent)
{

}


void Student::treat()
{
    qDebug()<<"请老师吃饭";
}

teacher.cpp

#include "teacher.h"

Teacher::Teacher(QObject *parent) : QObject(parent)
{

}

widget.cpp

#include "widget.h"
#include "ui_widget.h"

//Teacher 类 老师类
//Student 类 学生类
//下课后 ,老师会触发一个信号,饿了,学生响应信号,请客吃饭

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建一个老师对象 放到对象树中new Teacher(this) 就不用管他的释放了
    this->zt = new Teacher(this);
    //创建一个学生对象
    this->st = new Student(this);
    //老师饿了 学生请客的链接
    connect(zt,&Teacher::hungry,st,&Student::treat);

    //调用下课 函数
    classIsOver();

}

void Widget::classIsOver()
{
    //下课函数 调用后 触发老师饿了的信号
    //触发 emit 关键字
    emit zt ->hungry();

}


Widget::~Widget()
{
    delete ui;
}

六、 当自定义信号和槽发生重载时

6.1 解决函数重载的问题

利用函数指针 明确指向函数的地址:

void( Teacher:: * tSignal )( QString ) = &Teacher::hungry;

6.2 解决输出会带 “ ”的问题

在这里插入图片描述

QString 转 char *

  1. .ToUtf8() 转为 QByteArray
  2. .Data() 转为 char *

在这里插入图片描述

6.3 点击一个 下课的按钮,再触发下课 信号连接槽

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>

//Teacher 类 老师类
//Student 类 学生类
//下课后 ,老师会触发一个信号,饿了,学生响应信号,请客吃饭

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建一个老师对象 放到对象树中new Teacher(this) 就不用管他的释放了
    this->zt = new Teacher(this);
    //创建一个学生对象
    this->st = new Student(this);
//    //老师饿了 学生请客的链接
//    connect(zt,&Teacher::hungry,st,&Student::treat);

//    //调用下课 函数
//    classIsOver();

    //连接带参数的 信号和槽 信号发生重载了 要这样写
    //指针 -> 地址
    //函数指针 -> 函数地址  函数注意点:1.括号不可少 2.类型要匹配

    void(Teacher::*teacherSignal)(QString) = &Teacher::hungry;
    void(Student::*studentSlot)(QString) = &Student::treat;
    connect(zt,teacherSignal,st,studentSlot);
    classIsOver();

    //点击一个 下课的按钮,再触发下课  信号连接槽
    QPushButton * btn = new QPushButton("下课",this);
    //重置窗口的大小
    this->resize(600,400);

    //点击按钮 触发下课
    connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);

}

void Widget::classIsOver()
{
    //下课函数 调用后 触发老师饿了的信号
    //触发 emit 关键字
    //emit zt ->hungry();
    emit zt->hungry("宫保鸡丁");

}


Widget::~Widget()
{
    delete ui;
}


6.4 信号可以连接信号

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建一个老师对象 放到对象树中new Teacher(this) 就不用管他的释放了
    this->zt = new Teacher(this);
    //创建一个学生对象
    this->st = new Student(this);

    //无参信号和槽连接
    void(Teacher::*teacherSignal2)(void) = &Teacher::hungry;
    void(Student::*studentSlot2)(void) = &Student::treat;
    connect(zt,teacherSignal2,st,studentSlot2);

    //信号连接信号
    connect(btn,&QPushButton::clicked,zt,teacherSignal2);

    //断开信号 断开哪个复制哪个
    disconnect(zt,teacherSignal2,st,studentSlot2);
}

void Widget::classIsOver()
{
    //下课函数 调用后 触发老师饿了的信号
    //触发 emit 关键字
    //emit zt ->hungry();
    emit zt->hungry("宫保鸡丁");

}

6.5 断开信号 disconnect

//断开信号 断开哪个复制哪个
disconnect(zt,teacherSignal2,st,studentSlot2);

七、拓展

1.信号可以连接信号
2.一个信号可以连接多个槽函数
3.多个信号 可以连接 同一个槽函数
4.信号和槽函数的参数 必须类型一一的对应
5.信号和槽的参数个数 是不是要一致?信号的个数 可以多于槽函数的参数个数
比如void(Teacher::*teacherSignal)(QString) = &Teacher::hungry;
connect(btn,&QPushButton::clicked,zt,teacherSignal); 就不行,因为clicked参数是bool类型,后面是QString

6.Qt4版本以前的信号和槽连接方式
利用Qt4信号槽 连接无参数
connect(zt,SIGNAL(hungry()),st,SLOT(treat()));
Qt4版本优点,参数直观;缺点,类型不做检测 比如connect(zt,SIGNAL(hungry()),st,SLOT(treat(QString))); 这样错的,但是不报错

//emit zt ->hungry();
emit zt->hungry(“宫保鸡丁”);

}


## 6.5 断开信号 disconnect

```cpp
//断开信号 断开哪个复制哪个
disconnect(zt,teacherSignal2,st,studentSlot2);

八、Lambda表达式

8.1 Lambda介绍

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

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

[capture] (parameters) mutable ->return-type

{

statement

}

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

①、函数对象参数:

  1. [] ,标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
    空。没有使用任何函数对象参数。

  2. =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。

  3. &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。

  4. this。函数体内可以使用Lambda所在类的成员变量。

  5. a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。

    例如以下这个就是错误,因为没有传递btn2

    QPushButton * btn2 = new QPushButton;
    [btn](){
    	btn->setText("aaa");
    	btn2->setText("bbb"); // btn2看不到
    }();
    
  6. &a。将a按引用进行传递。

  7. a, &b。将a按值进行传递,b按引用进行传递。

  8. =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。

  9. &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。

②、操作符重载函数参数:

​ 标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。

③、可修改标识符:

​ mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝注意是能修改拷贝,而不是值本身)。

例如:[m] ()mutable { m = 100 + 10; qDebug() << m; }

不加mutable不可以修改

//mutable关键字 用于修饰值传递的遍历,修改的是拷贝,而不是本体
QPushButton * myBtn = new QPushButton (this);
    QPushButton * myBtn2 = new QPushButton (this);
    myBtn2->move(100,100);
    int m = 10;

    connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });

    connect(myBtn2,&QPushButton::clicked,this,[=] ()  { qDebug() << m; });

    qDebug() << m;

④、函数返回值:

​ ->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。

例如:

    int ret = []()->int{return 10;}();
    qDebug()<<"ret = "<<ret;

输出ret = 10

⑤、是函数体:

​ {},标识函数的实现,这部分不能省略,但函数体可以为空。

使用注意事项:

如果Lambda表达式后面不加()只是一个函数的声明,加了才是函数的调用。

    /*  
     [=](){
        btn->setText("aaa");
    } ;这样不行,这样只是一个函数声明,还要函数的调用,再最后加一个小括号
    
    */
    [=](){
        btn->setText("aaa");
    }();

8.2 案例

	//利用Lambda表达式 实现点击按钮 关闭窗口
	QPushButton * btn2 = new QPushButton;
	btn2->setText("关闭");
	btn2->move(100,0);
	btn2->setParent(this);
	connect(btn2,&QPushButton::clicked,this,[=](){
	    this->close();
	    emit zt->hungry("宫保鸡丁");
	});

九、练手

9.1 练手一

点击open打开第二个窗口,点击close关闭第二个窗口

在这里插入图片描述

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QPushButton>
#include <QDebug>
#include "secondwidget.h"

myWidget::myWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::myWidget)
{
    ui->setupUi(this);

    //open按钮,放到对象树中
    QPushButton * btn = new QPushButton(this);
    btn->setText("open");

    //close按钮,放到对象树中
    QPushButton * btn2 = new QPushButton(this);
    btn2->setText("close");
    btn2->move(200,200);

    //此处不能放到对象树中
    QWidget * second = new QWidget;

    connect(btn,&QPushButton::clicked,this,[=]{
        qDebug()<<"第二个画面出现";
        second->show();
    });
    connect(btn2,&QPushButton::clicked,second,&QWidget::close);
}

myWidget::~myWidget()
{
    delete ui;
}

9.2 练手二

点击open打开第二个窗口,按钮显示close,点击close关闭第二个窗口,按钮变open

在这里插入图片描述

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QPushButton>
#include <QDebug>

myWidget::myWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::myWidget)
{
    ui->setupUi(this);

    //按钮
    QPushButton * btn = new QPushButton(this);
    btn->setText("open");
    //第二个页面
    QWidget * second_widget = new QWidget;


//    QString text = btn->text();
//    qDebug()<<text;

    connect(btn,&QPushButton::clicked,this,[=]{
        if(btn->text() == "open")
        {
            second_widget->show();
            btn->setText("close");
            qDebug()<<btn->text();

        }
        else
        {
            second_widget->close();
            btn->setText("open");
            qDebug()<<btn->text();
        }

    });
    
}

myWidget::~myWidget()
{
    delete ui;
}


十、总结

在这里插入图片描述

信号需要声明,不需要实现;

槽函数需要声明,也需要实现;

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值