QT_信号和槽

Linux也有信号的概念,他是系统内部的通知机制,进程间的通信方式。

  • 信号源:谁发的信号
  • 信号的类型:哪种类别的信号
  • 信号的处理方式:注册信号处理函数,在信号被触发的时候自动调用执行

qt虽然和Linux不一样,但也有相似的地方。

  • 信号源:由哪个控件发出的信号
  • 信号的类型:用户进行不同的操作,就可能触发不同的信号(勾选复选框,选择下拉框)
  • 信号的处理方式:槽(slot)=>函数。qt中可以使用connect这样的函数,把一个信号和一个槽关联起来,在触发执行对应的操作。(Linux中,connect是tcp建立通信连接的,连接成功才能通信,这里面是qt里的,只是把信号和函数绑定,只要信号触发了,qt就自动执行槽函数,有点像回调函数,两个并没有什么关系)

connect

connect是QObject提供的静态的成员函数,qt提供的这些类,本身是存在一定的继承关系父类是QWidget,而QWidget又继承于QObject,QObject里面又不止QWidget一个子类。

cpp 复制代码
connect (const QObject *sender,
        const char *  signal  ,
        const QObject *  receiver  ,
        const char *  method  ,
        Qt::ConnectionType   type   = Qt::AutoConnection )
  • sender:信号的发送者;
  • signal:发送的信号(信号函数);
  • receiver:信号的接收者;
  • method:接收信号的槽函数;
  • type: 用于指定关联⽅式,默认的关联方式为 Qt::AutoConnection,通常不需要手动设定。

总结:信号又sender发出,发出的信号类型是signal,而信号由receiver接收,最后执行的处理函数是merhod

用法列如:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    button->move(200,200);
connect(button,&QPushButton::clicked,this,&Widget::close);
}

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

注意:connect要求,前两个参数是匹配的,我第一个参数是QPushButton 类型,但QPushButton继承于QWidget,QWidget继承于QObject,所以可以,第二个信号的参数必须是QPushButton内置的信号(父类的信号),不能是其他的类,如QLineEdit的信号,而close是Widget是自己内部有的槽函数,不需要我们自己在定义了,效果就是点击按钮实现窗口关闭。用法好像bind。

clicked和click的区别:clicked是点击完,click传入相当于调用

第二个和第四个参数是char* 但是我们传的是函数指针,这不是不一样的类型吗,是的这个是现在的传参方式,以前的时候得搭配一个给信号传参转成char*的SIGNA宏,给槽传参,搭配一个SLOT宏转成char*,写法就是connect(button,SIGNA(&QPushButton::clicked),this,SLOT(&Widget::close));,但是现在qt5就开始不这样写了,qt5对上述代码进行简化,给connect进行重载,重载版本就可以传入任何类型的参数

自定义槽

自定义槽函数非常关键,开发中大部分情况需要自定义槽函数,槽函数,就是用户触发某个操作后要进行的业务逻辑

手动代码创建,就是定义一个普通的成员函数,在Widget里面创建

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    button->move(100,100);
    connect(button,&QPushButton::clicked,this,&Widget::handleClicked);
}


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

void Widget::handleClicked()
{
    //按下按钮就修改窗口的标题
    this->setWindowTitle("按钮已经按下!");
}

在以前版本得用slots如这样public slots : xxxx 元编程,qmake构建qt项目的时候就是调用扫描器,扫描关键字,基于关键字再生成代码

第二种图形化生成

拖动pushButton按钮,然后右键转到槽,接着选择clicked,然后点击ok

此时就是生成一个函数,编写即可

cpp 复制代码
void Widget::on_pushButton_clicked()
{
     this->setWindowTitle("按钮已经按下!");
}

他在Widget里面有,但是没有调用connect建立关联,他在文件目录的上一级的ui文件下有on_pushButtom_clicked,qt在调用connectSlotsByName函数自动连接,

比较推荐使用图形化界面的方式创建,比较快速,也自动连接,如果通过代码的方式的话,还得手动connect连接(代码里没有connectSlotsByName)

自定义信号

qt中也允许自定义信号,自定义信号比较少见,实际开发中很少需要自定义信号,在GUI中,用户能够进行哪些操作是可以穷举的,qt内置信号中,基本上已经覆盖了上述所有可能的用户操作,因此使用qt内置的信号可以应付大部分场景。

  1. 信号是一个特殊的函数,我们只需要写出函数声明告诉qt这是一个信号即可,函数的定义是qt在编译过程中自动生成的,程序员无法干预
  2. 作为信号函数,函数的返回值必须是void
  3. 有没有参数都可以,甚至可以支持重载
  4. 声明时,用signals,qt特有的关键字(元编程技术,代码生成代码)

emit XXX 发出XXX信号

手动代码创建

图形化界面创建

.h不变,改cpp

带参信号和槽

信号和槽也可以带参数,当信号带有参数的时候,槽的参数必须和信号的参数一致,此时发射信号的时候,就可以给信号函数传递实参,与之对应的这个阐述就会被传递到对应的槽函数中,就可以达到给让信号给槽传参的效果了

.h

传参可以起到复用代码的效果,有多个逻辑,逻辑上整体一致,但是涉及到的数据不同,就可以通过函数-参数来复用代码,并且在不同的场景中传入不同的参数即可

上述代码两个都是点击事件,触发的都是同一个信号槽,然后在信号槽里发一个自定义信号,而接收到自定义信号后,又去调用自定义槽函数,达到随意点击按钮,切换标题的效果

qt中有很多内置的信号也是带参的,如:

注意了,参数类型必须一致,信号的参数必要要大于等于槽的参数个数,信号槽参数比信号多的话无法编译!这就意味着,一个信号可以绑定一个槽,而一个槽可以对应多个信号,可以应对多个情况,但是个数不一致的话,槽函数只能拿到按顺序的前N个参数

Q_OBJECT

qt中如果要让某个类能够使用槽函数,必须在类最开始的地方写下Q_OBJECT宏,会展开一系列复杂的代码

信号和槽存在的意义

信号槽目的就是解决响应用户的操作

信号槽的断开

使用disconnect来断开信号槽的连接,disconnect使用的方式和connect非常类似,大部分情况下把信号和槽连上了之后,就不必管了,主动断开往往是把信号重新绑定到另一个槽函数上。

单击修改标题窗口修改标题,单击修改窗口2,断开槽连接从新绑定,再次单击修改窗口直接把窗口改成窗口修改标题2,如果没有断开的话,就是两个槽绑定同一个信号,就执行两次

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
}

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

void Widget::handleClick()
{
    this->setWindowTitle("修改窗口标题");
}

void Widget::handleClick2()
{
    this->setWindowTitle("修改窗口的标题2");
}


void Widget::on_pushButton_2_clicked()
{
    //1.断开pushbutton原来的信号槽
    disconnect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
    //2.重新绑定信号槽
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick2);
}
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

    void handleClick();
 void   handleClick2();

private slots:
    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

用lambda定义槽函数

相关推荐
代码雕刻家8 小时前
编程语法细节
java·c语言·开发语言
j7~8 小时前
【C++】C++ 继承全解析:从基本语法到菱形继承的底层原理
开发语言·c++·继承·组合·虚继承·#暑假·七月创作之星博客挑战赛
数行拙笔8 小时前
C++客户端---String类型
开发语言·c++·bootstrap
影寂ldy9 小时前
C# Task 进阶:WaitAll / WaitAny / WhenAll / WhenAny
开发语言·c#
左左右右左右摇晃9 小时前
手写Tomcat原理整理
java·开发语言·笔记·tomcat
孫治AllenSun9 小时前
【JVM】四大引用类型分析
java·开发语言·jvm
看-是灰机9 小时前
使用go语言实现对接
linux·开发语言·后端·docker·语言模型·golang·飞书
人间凡尔赛18 小时前
Next.js 16 生产级实战:Cache Components + View Transitions 完整指南
开发语言·javascript·ecmascript
稚南城才子,乌衣巷风流1 天前
函数:编程中的核心概念
开发语言·前端·javascript