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
(本笔记整理自网路资源,侵删)