本节讲解信号和槽的概念、如何连接信号槽、标准信号槽的使用

1. 什么是信号槽

1.1 信号(SIGNAL)

什么是事件和信号,以QPushButton按钮的单击为例:

  • 按下按钮,会触发mousePressEvent事件,然后QPushButton会发射pressed()信号;
  • 松开按钮,会触发mouseReleaseEvent事件,然后QPushButton会发射released()信号和clicked()信号

常用的事件有很多,比如鼠标的单击和双击事件,鼠标的移动事件,键盘的输入事件等(后边专门有一个章节讲解事件)
当某个实例化的对象上产生这些事件时,该实例化对象就会发出特定的信号
信号的本质就是函数,并且是只需声明,无需实现的函数

具体一个类有哪些信号,可以查看Qt的帮助文档,以QPushButton为例:

首先,打开QPushButton的帮助说明,右侧只列出了其槽函数,它的信号都是继承自其父类QAbstractButton
qt-base

然后,跳转到其父类QAbstractButton,这里就有信号了,如下:
qt-base

最后,点击 “Signals” 可以跳转到信号处,如下:
qt-base

总结如下:

// 当按钮被点击(按下并抬起)之后,发送该信号,其中带有一个默认参数
// 对于QPushButton 通常不需要传递这个默认参数
// 对于可选中/取消选中的按钮,比如复选框QCheckBox、单选框QRadioButton 可以通过该参数,获取其是否选中
void clicked(bool checked = false);

// 当按钮被按下时,发送该信号
void pressed();

// 当按钮被抬起时,发送该信号
void released();

// 当按钮状态改变时,发送该信号,其中带有一个参数checked
// checked 用于标识复选框QCheckBox、单选框QRadioButton是否被选中
void toggled(bool checked);

由于这是我们第一次接触信号和槽,所以这里讲解的比较细致。以后遇到其他类的信号和槽,就按照这个方法来查阅即可。

1.2 槽(SLOT)

我们通常说的,就是槽函数
当点击了QPushButton按钮之后,通常需要执行对应的操作,比如让QWidget窗口最大/最小/正常化显示,或者关闭窗口
按照以上查看信号的方法,查看QWidget提供了哪些槽函数,如下:

// 最大化显示
void showMaximized();

// 最小化显示
void showMinimized();

// 正常显示
void showNormal();

// 关闭窗口
bool close();

1.3 连接信号槽

讲解了信号和槽之后,如何实现这种效果:点击按钮后,实现窗口的最大/最小/正常显示,或者关闭窗口。
答:这就需要将信号和槽使用connect函数进行连接

比如将QPushButton按钮的clicked()信号和QMainWindow窗口的close()槽函数建立连接
这样,当点击了QPushButton按钮后,Qt框架就会自动调用QWidget窗口的close()槽函数,从而实现窗口的关闭。

connect方法是QObject类的静态方法,它有多个重载的方法,如下:

QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)

bool disconnect(const QMetaObject::Connection &connection)
bool disconnect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
bool disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method)

连接信号和槽有多种写法,后面会有一节课专门讲解。不过总的来说,connect函数的一般形式如下:

connect(sender, signal, receiver, slot);

其中:

  • sender:发出信号的对象,比如QPushButton按钮
  • signal:发出的信号,比如clicked()
  • receiver:接收信号的对象,比如QWidget窗口
  • slot:接收到信号之后,调用的函数

一句话总结:信号槽是对象之间的信息通讯的方式

2. 案例演示

这里以一个实际的案例,来演示信号和槽的使用
案例:点击对应按钮,实现窗口的最大化/最小化/正常显示窗口,和关闭窗口
[图片]

2.1 新建工程

新建一个名为SignalAndSlot的项目
qt-base

2.2 界面布局

  • 放置按钮和弹簧:从左侧的Widget Box中,放置4个按钮和2个水平弹簧(避免按钮太大,影响美观)
  • 布局:选中窗口,点击工具栏的水平布局工具按钮,进行布局(这样可以对齐并且自适应窗口大小的变化);
  • 见名知意:修改控件的名字(objectName)
  • 修改控件属性:修改窗体的标题,修改按钮的显示文本以及字体大小

qt-base

2.3 连接信号槽

来到mywidget.cpp中,连接信号和槽,如下:

MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget) {
    ui->setupUi(this);

    // 连接信号和槽
    // 复制代码的两个快捷键:
    //      向前复制当前行:Ctrl + Alt + Up (向上箭头) 
    //      向后复制当前行:Ctrl + Alt + Down (向下箭头)
    connect(ui->btnMax, &QPushButton::clicked, this, &QWidget::showMaximized);
    connect(ui->btnNormal, &QPushButton::clicked, this, &QWidget::showNormal);
    connect(ui->btnMin, &QPushButton::clicked, this, &QWidget::showMinimized);
    connect(ui->btnClose, &QPushButton::clicked, this, &QWidget::close);
}
}

这样,点击按钮,就实现了预期的功能!

总结信号槽机制:
发送信号的对象(如按钮)不需要知道谁会接收这个信号,也不需要知道接收者会做什么。它只管发出“我被点击了”这个信号。
接收槽的对象(如窗口)也不需要知道信号是哪个对象发出的,只需要定义好当收到信号时该做什么。
因此,这种机制将交互的双方彻底解耦,使得代码的模块化、可复用性和可维护性大大提高。