Qt学习笔记(坐标系,信号,槽,lambda)

Qt中的坐标系
原点在左上角(0,0)点
横着是x轴的正向
竖着是y轴的正向
游戏里的坐标系原点在左下角
3D游戏还有Z轴

Qt中的信号和槽
在通话故事中有一个阿拉丁神的的故事,故事里有四样东西,首先有一个人,人捡了一个神灯。对神灯而言,想召唤出来东西要搓 擦。。。。它。对于灯而言,灯 冒烟。。。出来个灯神。对我们而言,里边主要的东西是人 。人擦了灯以后,灯就出来了个灯神。


需求:在做运行前项目时,点按钮没反应
点mybutton按钮  关闭窗口
可以抽离出四个东西:有一个按钮  -----> 被点击-------->窗口 ---------->进行关闭
把四个主要内容连接,连接的时候,英文单词是 connect。在代码中也是这个单词
connect()有四个参数
connect(参数1:信号的发送者,参数2:发送的信号(信号地址),参数3:信号的接收者,处理的函数(函数地址))

该函数专业而言叫槽函数
connect(按钮,点击,窗口,关闭)
四个参数进行连接

//点击我的按钮  关闭窗口
//信号关键字,signals

//clicked(bool checked=false)//有按下和弹起
//pressed()
//released()
//goggled(bool checked)布尔状态值 如灯 按下被打开了,再按,灯关闭了
//参数1 信号的发送者,参数2 发送的信号 参数3 信号的接收者, 参数4 处理的槽函数
//槽的单词是Slots,关闭函数close 有一个返回值,暂时用不上

connect(mybtn,&Mybutton::clicked,this,&MyWidget::close);
/注意:对应取地址  高版本做了优化,但是可以执行,但是建议写上&,加上&有自动提示信息
//用当前对象所属的类和他的父类都可以
//一行代码四个参数,很简单
connect(mybtn,&QPushButton::clicked,this,&QWidget::close);

信号和槽有另一个优点 : 松散耦合
对于我们的按钮  点击必须要关闭窗口吗 不是 关闭窗口必须要点击按钮才能实现吗 不是
左边的事情和右边的事情是松散的 用connect把两边的事物连接在一起

自定义的信号和槽函数

 
自定义信号函数
 //自定义信号 需要写在signals下边
 //返回类型必须是void
 //信号只需要声明,不需要实现 (重点) 声明完就暂时不用管了
 //信号可以有参数,可以重载
    
自定义槽函数 (和信号的唯一区别是 需要声明也需要实现)

//自定义槽函数的地方,比较低的版本必须写在slots下 高版本可以写到public(上边的public) 或者全局函数
//返回类型必须是void
//信号需要声明,也需要实现  在cpp文件下实现,这一点区别去信号函数,
//槽函数也可以有参数,可以发生重载

信号触发关键字 emit
当自定义信号和自定义槽出现函数重载的时候,相应的信号函数和槽函数的地址就不明确了。
所以需要一个函数指针来指向函数地址
QString 转 char*  toUtf8 转 QByteArray 类型 再利用data 转 char*

信号和槽可以断开 disconnect(,,,);

Lambda表达式  (匿名函数的形式)//创建匿名的函数对象,以简化编程工作。

C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。首先看一下Lambda表达式的基本构成:
[capture](parameters) mutable ->return-type
{
    statement
}
[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}

① 函数对象参数;
[],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
1.    空。没有使用任何函数对象参数。
2.    =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
3.    &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
4.    this。函数体内可以使用Lambda所在类中的成员变量。
5.    a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
6.    &a。将a按引用进行传递。
7.    a, &b。将a按值进行传递,b按引用进行传递。
8.    =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
9.    &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
② 操作符重载函数参数;
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
③ 可修改标示符;
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的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
⑤ 是函数体;
{},标识函数的实现,这部分不能省略,但函数体可以为空。

[](){} []中括号里可以加一些东西 小括号里是参数,大括号之前可以加一些关键字 大括号里具体实现的内容
写lambda表达式运行不起来,证明C++版本太低,
要在.pro文件中加入 CONFIG +=C++11
[](){}
[]内部=值传递 推荐 & 引用传递 不推荐 如果有锁的情况下程序会崩
()参数
{}函数实现体
mutable改变值传递的内部变量
返回值 []()->type{};

 

实践代码

//.pro文件
#-------------------------------------------------
#
# Project created by QtCreator 2019-08-16T09:30:33
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = 02_SignalsAndSlot
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        student.cpp \
        teacher.cpp \
        widget.cpp

HEADERS += \
        student.h \
        teacher.h \
        widget.h

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
//main.cpp
#include "widget.h"
#include 

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

    return a.exec();
}
//student.cpp
#include "student.h"
#include 

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

}

void Student::treat()//请客
{
    qDebug()<<"学生请老师吃饭。";
}

void Student::treat(QString foodName)
{

     //qDebug()<<"请老师吃饭,老师要吃:"<
//student.h
#ifndef STUDENT_H
#define STUDENT_H

#include 

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

signals:

public slots:
    //自定义槽函数的地方,比较低的版本必须写在slots下 高版本可以写到public(上边的public) 或者全局函数
    //返回类型必须是void
    //信号需要声明,也需要实现  在cpp文件下实现,这一点区别去信号函数,
    //槽函数也可以有参数,可以发生重载
    void treat();//请客


    void treat(QString foodName);
};

#endif // STUDENT_H

 

//teacher.cpp
#include "teacher.h"

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

}
//teacher.h
#ifndef TEACHER_H
#define TEACHER_H

#include 

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

signals:
    //自定义信号 需要写在signals下边
    //返回类型必须是void
    //信号只需要声明,不需要实现
    //信号可以有参数,可以重载
    void hungry();

    void hungry(QString fonndName);//要吃什么

public slots:


};

#endif // TEACHER_H
//widget.cpp  主要
#include "widget.h"
#include 
#include 
//需求: 创建两个类 Teacher类 Student类
//下课后 老师Teacher zz 会发出一个信号 饿了
//学生响应信号Student st 并处理信号的槽函数 请客吃饭
//QObject是最顶层的类 Teacher 和 Student都继承他

//饿了信号是自定义信号  写在signals下





Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    zz=new Teacher(this);//把老师对象放入children表中
    st=new Student(this);//不写 需要在两个类里写析构函数,自己释放
    //连接老师和学生
    //connect(zz,&Teacher::hungry,st,&Student::treat);  //无参的槽函数
    //此时运行不会打印消息,因为没有时机发送信号,信号没有被发送
    //下课后老师才会饿了
    //在widget.h里补充一个函数


//有参数信号和槽连接
//    void(Teacher::*teacherSignal)(QString)=&Teacher::hungry;
//    void(Student::*stSolt)(QString)=&Student::treat;

//    connect(zz,teacherSignal,st,stSolt);

    //做的参数是一个函数地址,什么能指向函数的地址: 函数指针
    //所以上边函数可以提炼出来当右值


    //classIsOver();
   //点击按钮下课
    QPushButton * btn= new QPushButton("下课",this);//未使用的变量的警告不用理会
    //触发无参的信号和槽
    //信号连接 信号
    void(Teacher::*noTeacherSignal)(void)=&Teacher::hungry;
         void(Student::*noStSlot)(void)=  &Student::treat;

    connect(btn,&QPushButton::clicked,zz,noTeacherSignal);//此处需要明确地址
   // connect(btn,&QPushButton::clicked,this,&Widget::close);一个信号可以连接多个槽
    connect(zz,noTeacherSignal,st,noStSlot);
    //写个两个connect
    //第一个是btn连接老师 zz  点击btn的clicked 触发 zz   zz又连接槽  连锁反应

    //断开信号和槽
   // disconnect(zz,noTeacherSignal,st,noStSlot);//切断信号和槽的连接
    //信号和槽的拓展
    //1.信号可以连接信号
    //2.信号和槽可以断开
    //3.一个信号可以触发多个槽函数  //发工资 买手机,买项链,买钻戒
    //4.多个信号可以连接同一个槽 //多个按钮,分别都可以实现关闭功能
    //5.信号和槽的参数必须一一对应,,否则编译过不去
    //参数的个数必须一一对应吗? 不一定
     //void(Teacher::*noTeacherSignal)(void,int,double)=&Teacher::hungry;
    //信号的参数个数可以多于槽函数的参数个数,但是必须类型一一对应,反之不可以


    //Qt 4版本的 信号和槽写法 (不推荐使用)
    //关键字没有变conncet();
    connect(zz,SIGNAL(hungry(QString)),st,SLOT(treat(QString)));
    //不推荐Qt4版本信号和槽写法,类型匹配不检测
    //优点:参数的类型比较直观,发生了重载的时候也不用写函数指针,保证参数匹配也可以使用
    //不检测原因,SIGNAL和SLOT下 会把里面代码作为字符串处理
    //SIGNAL("hungry(QString)")  SLOT("treat(QString)")
    //推荐Qt5的写法,比较严格
    connect(zz,SIGNAL(hungry(QString)),st,SLOT(treat(Void)));//类型不匹配,有参的匹配了一个无参数
    connect(zz,SIGNAL(hungry(Void)),st,SLOT(treat(String)));    //类型不匹配,编译时可以通过,运行时报错
    classIsOver();

    //重置大小
    this->resize(100,100);


    //Lambda表达式

    QPushButton *btn2 = new QPushButton("aaaa",this);
//    [=](){    //=方式   btn2是以值传递方式传进来的 ,如同拷贝一份数据
//    []代表可以用值传递方式让匿名函数内部看到btn2,不加=找不到btn2,编译不通过
//      btn2->setText("bbbb");
//    };//这个叫函数的声明
//    [=](){    //=方式   btn2是以值传递方式传进来的 ,如同拷贝一份数据
//      btn2->setText("bbbb");
//    }();//加上()才是匿名函数的调用

//    [btn2](){    //=方式   btn2是以值传递方式传进来的 ,如同拷贝一份数据
//      btn2->setText("bbbb");
//     //btn->setText("cccc");//此时btn看不到
//    }();//加上()才是匿名函数的调用

    [=](){    //=方式   btn2是以值传递方式传进来的 ,如同拷贝一份数据
      btn2->setText("bbbb");
    }();//加上()才是匿名函数的调用



    //mutable
    QPushButton * myBtn = new QPushButton (this);
    QPushButton * myBtn2 = new QPushButton (this);
       myBtn2->move(200,100);
       int m = 10;
       //mutable关键字用于修改值传递的变量  进行修改,用途很少
       connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });

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

       qDebug() << m;

    //返回值, lambda是匿名函数,可以有返回值

       int ret=[]()->int{return 100000;}();
       qDebug()<<"ret="<move(150,150);
//       connect(btn3,&QPushButton::clicked,this,[=](){
//           btn3->setText("hahahhahhah");
//           qDebug()<<"btn3被点击了";
//       });

       QPushButton *btn3=new QPushButton("btn3",this);
       btn3->move(150,150);
//       //做信号槽连接的时候,默认内部变量会进行所状态,即只读状态,如果进行了写操作,会挂掉,所以以后都用=方式就可以,并没有太大开销
//       connect(btn3,&QPushButton::clicked,this,[&](){ //有时会出bug
//           btn3->setText("hahahhahhah");
//           qDebug()<<"btn3被点击了";
//       });

       this->resize(500,500);
//无参的按钮,调用有参的 请客吃饭
       connect(btn3,&QPushButton::clicked,this,[=](){
          zz->hungry("红焖大虾");
       });

 //点击按钮 关闭窗口 用lambda表达式写会非常的方便
//       connect(btn3,&QPushButton::clicked,this,[=](){
//           this->close();
//       });

 //如果connect第三个参数是this 那么this可省
       connect(btn3,&QPushButton::clicked,[=](){
           this->close();
       });

}


void Widget::classIsOver(){
    //触发老师饿了的信号
    //老师饿了的信号属于自定义信号,触发自定义信号关键字是 emit
    emit zz->hungry();//触发无参的信号
    //此时运行还不会打印,因为函数只是做了声明,没有调用的地方。
    //回到上边去调用此函数。
     emit zz->hungry("红烧排骨");

}


Widget::~Widget()
{

}
//widget.h

#ifndef WIDGET_H
#define WIDGET_H



#include "teacher.h"//包含两个头文件
#include "student.h"

#include 

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
    Teacher *zz;   //声明两个对象,构建时写在这里
    Student *st;   //写到成员属性里为了其他类便于访问

    void classIsOver();



};

#endif // WIDGET_H

(本笔记整理自网路资源,侵删)

你可能感兴趣的:(Qt)