【Qt】信号与槽

文章目录

  • 信号与槽
    • [1. connect](#1. connect)
    • [2. 槽 slot](#2. 槽 slot)
    • [3. 信号 signal](#3. 信号 signal)
    • [4. 带参数的信号和槽](#4. 带参数的信号和槽)
    • [5. 信号槽机制的意义](#5. 信号槽机制的意义)

信号与槽

在Qt中,用户和控件的每一次交互都称为一个"事件"。比如,用户点击按钮,用户关闭窗口。每一次事件的发生,都会触发一个信号 。信号的作用就是用来区分用户的操作类型,并针对用户的操作进行不同的处理,这种不同的处理就是

信号源:信号是由谁发出的?

信号类型:不同信号的类型

接收者:信号由谁处理?

:对信号作出的响应动作

1. connect

Qt中提供connect函数,用于绑定信号与槽,即先准备好信号的处理方式,才能在与用户交互过程中处理对应的信号。

cpp 复制代码
QObject::connect
    (const QObject *sender,
     const char *signal, 
     const QObject *receiver, 
     const char *method, 
     Qt::ConnectionType type = Qt::AutoConnection)

sender: 信号源

signal: 信号类型

receiver: 负责信号接收处理者

method: 处理信号的方式

上面是旧版本Qt中的connect函数,因为signal和method的参数类型都是char*,但信号函数和槽函数的类型是变化的,所以每次传参还需要调用SIGNAL()SLOT()宏函数将函数指针转换为char*类型,新版本Qt提供了connect函数的泛型版本:

cpp 复制代码
template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(
    const typename QtPrivate::FunctionPointer<Func1>::Object *sender, 
    Func1 signal,
	const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, 
    Func2 slot,
    Qt::ConnectionType type = Qt::AutoConnection)
  1. 这是一个模板函数,signalslot的类型由传入参数决定。
  2. senderreceiver分别是Func1Func2类型萃取后的结果,保证了sender中必须包含signal信号,receiver中必须包含slot槽函数,若不存在对应的信号或槽,程序报错,避免程序员手动检查

注意:

  1. click是一个槽函数,clicked是一个信号

  2. 一个信号绑定多个槽函数时,信号发生,多个槽函数同时被调用。

  3. disconnectconnect作用相反,断开信号与槽的连接,使用方式与connect类似。一般使用disconnect主动断开信号与槽的连接,是为了将信号绑定到另一个槽函数上。

2. 槽 slot

槽本质上就是一个成员函数。槽函数不仅有Qt内置的,还可由用户自定义,这是开发中的关键,因为不同的业务,对于用户的交互有不同的处理逻辑。那么,如何定义槽函数呢?

  1. 跟定义一个普通的成员函数没有区别

    widget.h

    widget.cpp

  2. 图形化操作,对于生成的控件,选择信号,并自动生成槽函数的声明和槽函数的定义,槽函数的函数体由用户设计(默认为什么都不做),而信号与槽函数的绑定是由Qt自动完成的(connectSlotsByName)。

最终ui_widget.h中调用connectSlotsByName()函数,根据槽函数名称,实现槽函数与信号之间的绑定

3. 信号 signal

信号本质上也是一个函数,而且也可以由用户自定义,不过自定义信号比较少见,一般Qt内置的信号类型就足以应对大部分开发场景。emit 通过代码发射信号

  1. 自定义类Widget的信号函数mySignal(),记得加上signals关键字修饰(函数的定义是Qt生成的,我们只需写函数声明)
  1. 写一个槽函数handleMySignal(),用于处理自定义信号

    cpp 复制代码
    void Widget::handleMySignal()
    {
        this->setWindowTitle("mySignal信号:修改标题");
    }
  2. 调用connect连接自定义的信号和槽

    cpp 复制代码
    connect(this, &Widget::mySignal, this, &Widget::handleMySignal); // Widget类的构造函数中
  3. 随便整一个按钮(这里用了图形化界面生成),点击它会发射自定义信号mySignal,验证效果

    cpp 复制代码
    void Widget::on_pushButton_2_clicked()
    {
        emit mySignal(); //发射自定义信号, emit可以省略
    }

4. 带参数的信号和槽

  1. 信号函数可以向槽函数传递参数,以实现代码复用;

  2. 信号函数的参数类型必须和槽函数的参数类型一致,才能传递,否则会报错;

  3. 信号函数的参数个数 >= 槽函数的参数个数,这使得信号与槽之间的连接更加灵活,即一个槽函数可以与被多个不同的信号函数连接,只要保证每个信号函数传递的参数不少于槽函数的参数个数即可,槽函数会按顺序取前N个(N是槽函数的参数个数),多余的参数自动丢弃;

  4. 带参数的信号与槽

cpp 复制代码
//助于理解的伪代码
void Widget::mySignal1(QString t1); //signal

void handleMySignal(const QString& s); //slot
void Widget::handleMySignal(const QString& s)
{
    qDebug() << s;
}

connect(this, &Widget::mySignal1, this, &Widget::handleMySignal); //连接信号槽


void Widget::on_pushButton_2_clicked() //发射信号
{
    emit mySignal1("mySignal1");
}

最后执行情况:

  1. 参数个数不一致的信号与槽

    cpp 复制代码
    //助于理解的伪代码
    void Widget::mySignal1(QString t1); //signal
    void Widget::mySignal2(QString t1, QString t2);//signal
    
    void handleMySignal(const QString& s); //slot
    void Widget::handleMySignal(const QString& s)
    {
        qDebug() << s;
    }
    
    connect(this, &Widget::mySignal1, this, &Widget::handleMySignal); //连接信号槽
    connect(this, &Widget::mySignal2, this, &Widget::handleMySignal);
    
    void Widget::on_pushButton_2_clicked() //发射信号
    {
        emit mySignal1("mySignal1");
        emit mySignal2("mySignal2", "mySignal2");
    }

    最终执行结果,可以看到发送mySignal2也是只打印一次,因为槽函数handleMySignal只取mySignal2第一个参数


5. 信号槽机制的意义

  1. 解耦合

    信号函数的定义,槽函数的定义,以及信号槽的连接是分开处理的,耦合度低。

  2. 多对多

    一个信号可以绑定多个槽函数,一个槽函数也可以被多个信号绑定。

    cpp 复制代码
    QPushButton* btn3 = new QPushButton(this);
    btn3->setText("点击此按钮,会发生三件事!");
    btn3->move(0, 200);
    
    connect(btn3, &QPushButton::clicked, this, &Widget::mySlot1);
    connect(btn3, &QPushButton::clicked, this, &Widget::mySlot2);
    connect(btn3, &QPushButton::clicked, this, &Widget::mySlot3);

相关推荐
努力可抵万难5 分钟前
【算法系列】leetcode1419 数青蛙 --模拟
c++·算法·模拟
Ciderw8 分钟前
MySQL日志undo log、redo log和binlog详解
数据库·c++·redis·后端·mysql·面试·golang
YH_DevJourney15 分钟前
Linux-C/C++《C/9、信号:基础》(基本概念、信号分类、信号传递等)
linux·c语言·c++
终极定律42 分钟前
qt:输入控件操作
开发语言·qt
FL16238631291 小时前
[C++]使用纯opencv部署yolov12目标检测onnx模型
c++·opencv·yolo
JenKinJia1 小时前
Windows10配置C++版本的Kafka,并进行发布和订阅测试
开发语言·c++
wen__xvn2 小时前
每日一题洛谷P1914 小书童——凯撒密码c++
数据结构·c++·算法
云中飞鸿2 小时前
MFC中CString的Format、与XML中的XML_SETTEXT格式化注意
xml·c++·mfc
小小小白的编程日记3 小时前
List的基本功能(1)
数据结构·c++·算法·stl·list
努力可抵万难3 小时前
C++11新特性
开发语言·c++