zl程序教程

您现在的位置是:首页 >  其他

当前栏目

QT入门基础(二)

2023-04-18 14:24:45 时间

四.信号和槽机制

  • 概念:
  1. 信号:各种事件,当某个事件发生后,如某个按钮被点击了一下,它就会发出一个被点击的信号(signal)
  2. 槽: 响应信号的动作,某个对象接收到这个信号之后,就会做一些相关的处理动作(称为槽slot)
  3. 链接:Qt对象不会无故收到某个信号,要想让一个对象收到另一个对象发出的信号,这时候需要建立连接(connect),即建立信号和槽的关系

1.系统信号和槽

  • 示例:建立点击按钮和窗口关闭的联系
QPushButton *quitBtn = new QPushButton("按钮1",this);
connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);
  • 效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d8e2wisB-1678792590074)(https://typoracole.oss-cn-guangzhou.aliyuncs.com/img/信号和槽链接.gif)]

connect函数是建立信号发送者、信号、信号接收者、槽四者关系的函数:connect(sender, signal, receiver, slot);

  • 参数解释:
  1. sender:信号发送者
  2. signal:信号
  3. receiver:信号接收者
  4. slot:接收对象在接收到信号之后所需要调用的函数(槽函数)

注意:connect的四个参数都是指针,信号和槽是函数指针

  • 系统自带的信号和槽的查找:

image-20230314152727168

  1. 在帮助文档中比如我们上面的按钮的点击信号,在帮助文档中输入QPushButton
  2. 首先我们可以在Contents中寻找关键字 signals,但是我们发现并没有找到,这时候我们应该想到也许这个信号的被父类继承下来的
  3. 因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个

image-20230314152804758

这里的clicked就是我们要找到,槽函数的寻找方式和信号一样,只不过他的关键字是slot

2.自定义信号和槽

Qt框架默认提供的标准信号和槽不足以完成我们日常应用开发的需求,但是Qt信号和槽机制提供了允许我们自己设计自己的信号和槽

  • 自定义信号:
  1. 声明在类的signals域下
  2. 没有返回值,void类型的函数
  3. 只有函数声明,没有定义
  4. 可以有参数,可以重载
  5. 通过emit关键字来触发信号,形式:emit object->sig(参数);
  • 自定义槽函数:
  1. qt4 必须声明在 private/public/protected slots域下面,qt5之后可以声明public下,同时还可以是静态的成员函数,全局函数,lambda表达式
  2. 没有返回值,void类型的函数
  3. 不仅有声明,还得要有实现
  4. 可以有参数,可以重载
  • 使用自定义信号和槽示例:

定义场景:下课了,老师跟同学说肚子饿了(信号),学生请老师吃饭(槽)

  • 老师类中声明信号:
signals:
void hungry();
  • 学生类中声明槽:
public slots:
void treat();
  • 学生类实现槽函数:
void Student::treat()
{
    qDebug() << "Student treat teacher";
}
  • 信号和槽链接:使用connect
teacher = new Teacher(this);
student = new Student(this); connect(teacher,&Teacher::hungury,student,&Student::treat);
  • 信号触发:在窗口中声明一个公共方法下课,这个方法的调用会触发老师饿了这个信号,而响应槽函数学生请客
void MyWidget::ClassIsOver()
{
  //发送信号
  emit teacher->hungry();
}
  • 带参数的信号和槽:
void hungry(QString name); //自定义信号

void treat(QString name );  //自定义槽

对于有两个重名的自定义信号和自定义的槽,直接连接会报错,所以需要利用函数指针来指向函数地址, 然后再做连接

//函数重载 函数指定具有二义性需要进行指定
//1.函数指针赋值
void(Teacher::*tqs)(QString)=&Teacher::hungry;//自动赋值对应类型的函数地址
void(Student::*sqs)(QString)=&Student::treat;
connect(pTeacher,tqs,pStudent,sqs);
//2.使用static_cast转换
connect(pTeacher,
            static_cast<void(Teacher::*)()>(&Teacher::hungry),
            pStudent,
            static_cast<void(Student::*)()>(&Student::treat));
//3.qt4之前使用宏转换
connect(pTeacher,SIGNAL(hungry()),pStudent,SLOT(treat()));
connect(pTeacher,SIGNAL(hungry(QString)),pStudent,SLOT(treat(QString)));
  • 效果:

image-20230314164119460

注:对于使用SIGNAL()和SLOT()宏指定函数,虽然使用简单,但是宏只是做字符串替换,编译时不会检查,所以运行时可能报错

3.信号和槽的拓展

  1. 一个信号可以和多个槽相连

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

  1. 多个信号可以连接到一个槽

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

  1. 一个信号可以连接到另外的一个信号

当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。这里还是使用connect函数,只是信号的接收者和槽函数换成另一个信号的发送者和信号函数

  1. 信号和槽可以断开连接

使用disconnect函数,当初建立连接时connect参数怎么填的,disconnect里边4个参数也就怎么填。当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。

  1. 信号和槽函数参数类型和个数必须同时满足两个条件

信号函数的参数个数必须大于等于槽函数的参数个数

信号函数的参数类型和槽函数的参数类型必须一一对应

4.槽函数使用Lambda表达式

  • 以QPushButton点击事件为例:
connect(btn,&QPushButton::clicked,[=](){
    qDebug()<<"Clicked";
});
  • 效果:

image-20230314164904006

  • 说明:
  1. 使用Lambda表达式作为槽的时候不需要填入信号的接收者
  2. 当点击按钮的时候,clicked信号被触发,lambda表达式也会直接运行
  3. 当然lambda表达式还可以指定函数参数,这样也就能够接收到信号函数传递过来的参数了
  4. 由于lambda表达式比我们自己自定义槽函数要方便而且灵活得多,所以在实现槽函数的时候优先考虑使用Lambda表达式
  5. 一般使用习惯也是lambda表达式外部函数的局部变量全部通过值传递捕获进来,也就是:[ = ] ( ) { }的形式

表达式也会直接运行
3. 当然lambda表达式还可以指定函数参数,这样也就能够接收到信号函数传递过来的参数了
4. 由于lambda表达式比我们自己自定义槽函数要方便而且灵活得多,所以在实现槽函数的时候优先考虑使用Lambda表达式
5. 一般使用习惯也是lambda表达式外部函数的局部变量全部通过值传递捕获进来,也就是:[ = ] ( ) { }的形式