Qt_02

信号和槽

信号和槽,本质都是事件,或者说是某种动作,一个触发一个响应

信号

信号一般写在头文件中,也就是中,需要关键字signals限定,就像privateprotectedpublic
此外信号有几个特点

  1. 只返回void
  2. 只需要声明,不需要实现,即函数体
  3. 自定义信号可以重载

teacher.h举个例子

#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>

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


    //自定义信号
signals:
    void hungry();
    void hungry(QString str);

};

#endif // TEACHER_H




同样的,一般函数也是写在中;在之前的版本中,需要用public slots进行说明,但后来的Qt则可以将槽函数直接写在public下,就想当于公有成员函数

槽函数的几个特点

  1. 只返回void
  2. 需要声明以及实现
  3. 可以重载

student.h中的声明槽函数

#ifndef STUDENT_H
#define STUDENT_H

#include <QObject>

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

public slots:
    void treat();
    void treat(QString str);

};

#endif // STUDENT_H

student.cpp实现槽函数

#include "student.h"
#include <QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}

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

void Student::treat(QString str){

    // 将QString转换为char*  这样输出就不会有双引号
    // 先调用.toUtf8() 转换为QByteArray类型 再用.data()转换为char* 类型
    qDebug()<<"请老师吃:"<< str.toUtf8().data();
}




connect()

信号通过函数connect()进行连接,而这个函数还有两个参数,那就是信号和槽对应的对象


这里在widget.h定义了teacherstudent的对象

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include "teacher.h"
#include "student.h"
class Widget : public QWidget
{
    Q_OBJECT

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

    Teacher * zt;
    Student * st;

    void classIsOver();
};
#endif // WIDGET_H

下面在widget.cpp中进行connect操作

简单分析一下,zt发出hungry的信号,st就响应,进行treat操作

	connect(zt,&Teacher::hungry,st,&Student::treat);

但编译运行后发现,并没有成功输出treat()中的qDebug() << "请老师吃饭";



其实这里就像把开关接在灯上,连接电源,我们只需要按下按钮,灯就会亮
开关发送被按下的信号,响应,进行操作
而只有按下事件发生,开关才会被按下吧,灯才会亮


触发信号

void Widget::classIsOver(){
    // 自定义信号触发 关键字    emit
    //emit zt->hungry();
    emit zt->hungry("宫保鸡丁");
}



widget.cpp中,完整的代码如下

#include "widget.h"

#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    zt = new Teacher(this);
    st = new Student(this);

	connect(zt,&Teacher::hungry,st,&Student::treat);		
    classIsOver();
}

void Widget::classIsOver(){
    // 自定义信号触发 关键字    emit
    emit zt->hungry();
}

Widget::~Widget()
{
}




disconnect()

connect()一样的操作




重载信号和槽

重载的定义就不用说了,这里主要讲重载后如何连接

	void hungry(QString str);

	void treat(QString str){
		qDebug() << "请老师吃" << str;
	}

	void Widget::classIsOver(){
    	emit zt->hungry("鸡肉滑蛋饭");		// !!!
	}

	

	void (Teacher::*noParaT)(QString) = &Teacher::hungry;	// 函数指针 因为信号重载了
    void (Student::*noParaS)(QString) = &Student::treat;		
    connect(zt,noParaT,st,noParaS);   

当自定义的信号和槽发生重载后,就必须用函数指针,明确指出函数地址



这里有一点需要注意,信号和槽的参数类型必须一一对应,所以重载就必须同时重载

但信号函数的参数个数可以多于槽函数的参数个数
这里就比如老师要鸡肉滑蛋饭三份,而学生可以只接收前者





拓展


信号可以连接信号

	QPushButton *btn = new QPushButton(this);
	btn->setText("classIsOver");
	
	void (Teacher:: *teachersignal2)() = &Teacher::hungry;
    void (Student:: *studentslot2)() = &Student::treat;
    
    connect(btn,&QPushButton::clicked,zt,teachersignal2);
    connect(zt,teachersignal2,st,studentslot2);
    



一个信号连接多个槽

classIsOver后,顺便关闭窗口

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

通常来说,一个信号连接上多个槽并没有问题,但有个问题就是,没办法控制槽函数的执行顺序



多个信号可以连接同一个槽

这里定义多个btn,都表示下课是没问题的

	QPushButton *btn2 = new QPushButton(this);
    btn2->setText("放学了");
    btn2->setGeometry(200,300,80,40);
    QPushButton *btn3 = new QPushButton();
    btn3->setParent(this);
    btn3->setText("誒我就是逃");
    btn3->move(300,200);
    
    connect(btn2,&QPushButton::clicked,zt,teachersignal2);
    connect(btn3,&QPushButton::clicked,zt,teachersignal2);



上面几个拓展,都是QPushButton的实例btn通过clicked触发信号hungry,所以也就不需要起初的那个classIsOver()触发信号的函数了




Qt4的信号和槽

	connect(zt,SIGNAL(hungry(QString)),st,SLOT(treat(QString)));

这个之所以被淘汰了,就是因为有个缺点,对参数类型不做检测
编译时可以通过,但运行却出毛病了

原因在于,SIGNALSLOT括号下的内容,转换为字符串

	SIGNAL("hungry(QString)")
	SLOT("treat(QString)")

都转换成字符串了,还做啥参数类型对比?




lambda书写槽函数

	QPushButton *btn5 = new QPushButton(this);
    int n=1;
    connect(btn5,&QPushButton::clicked,this,
    [=]()mutable{btn5->setText("aaaaa");btn5->move(n*100,n*100);++n;} );

lambda表达式,要修改捕获的参数n,别忘了加上关键字mutable

mutable,可修改标识符。按值传递进行捕获参数时,加上mutable后可以修改值传递进来的拷贝,注意是修改拷贝,而不是修改值本身



在这里,捕获按钮并进行setText,只能用值传递=,若是用引用传递&就会报错,原因是,Qt的按钮会有一种🔒状态,也就是只读状态,而这里还能进行setTextmove,但其实操作的并不是一开始的那个btn5,而是拷贝;

而这两个操作并不需要mutable关键字声明



mutable一些补充

	int m=10;
    QPushButton *btn6 = new QPushButton(this);
    btn6->move(100,200);
    connect(btn6,&QPushButton::clicked,this,[=]()mutable{ m=m+100;qDebug()<<m;});
    
    QPushButton *btn16 = new QPushButton(this);
    btn16->move(200,200);
    connect(btn16,&QPushButton::clicked,this,[=](){qDebug()<<m;});




返回

	int n=10;
	n = [=]()->int{ return 999;}  () ;
	qDebug() << n;

lambda需要返回,就用-> return_type

另外,lambda表达式,就是定义了一个函数,要调用就和普通函数一样,再加一对小括号()




不对等参数类型的connect()

lambda表达式,就可以实现不需要信号和槽参数类型必须一一对应

	connect(btn,&QPushButton::clicked,this,[=](){
			this->treat("鸡肉滑蛋饭");
	});

其中信号clicked()其实是有一个bool类型的参数的

还有一个骚操作,如果connect()第三个参数是this且第四个参数是lambda表达式,那第三个参数可以省略




在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值