【QT】信号与槽基础:手动连接的原理与实践

信号与槽的基本概念

信号(signals)

在Qt框架中,用户与控件的每次交互都会触发相应的事件,并产生对应的信号。例如,当用户点击按钮时,按钮会发出"按钮被按下"的信号。

在QT中要注意信号的三个要素:

1.信号源:哪个控件发出的信号。

2.信号类型:不同用户操作会触发不同信号。例如:按钮点击信号、窗口关闭信号。

本质:GUI程序的核心在于实现用户交互,需要准确识别并响应各类用户操作。

3.信号处理的方式:槽(slot)函数,后续只要信号发出了,qt会自动执行槽函数。

槽(slot)

Qt 控件天生具备接收信号的能力,且单个控件可以同时响应多个不同信号。当控件接收到特定信号时,就会执行对应的响应操作,这种响应机制在 Qt 中被称为"槽"。槽函数在做的本质上对未发生的事件写应对方案,事件发生了(信号发出)就执行先前写好的方案(自动调槽函数)。举个实际的例子:设置手机闹钟------你现在定义"早上7点响铃"(写槽函数),当时间到达7点时(信号发出),系统自动执行响铃操作(调用槽函数)。

槽函数本身是一种回调函数------------回调函数是一种编程模式,指将函数A作为参数传递给函数B,由函数B在特定条件满足时调用函数A。这种机制实现了"调用权"的转移。回调函数是编程中常用的机制,例如c++的仿函数,linux中的线程入口函数。

为了对信号和槽有更深刻的理解我们来画图:

connect函数

从上图中我们可以知道,如果不把信号和槽函数关联到一起,自然槽函数是不能在信号发出时被自动调用的。qt提供了connect函数,我们可以通过connect函数将信号和槽关联到一起。

QObject

connectQObject类提供的静态成员函数。由于Qt中所有类都继承自QObject,且遵循高内聚、低耦合的设计原则,子类可以自然地继承并使用父类的成员函数。QT中的继承关系如图:

connect

选中QObject按F1在文档中查询,我们会在成员函数这一栏看到connect。

cpp 复制代码
connect(const QObject *sender,//哪个控件发的信号
 const char *signal,//信号的类型
 const QObject * receiver ,//接收信号的对象,普通槽函数必须写,Lambda表达式不用写
 const char *method, //怎么处理信号
Qt::ConnectionType type = Qt::AutoConnection) const//第四个参数后面学到在解释

使用QT创建槽函数

接下来我们来使用connect函数连接信号和槽:

widget.cpp

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(300,200);//设置坐标

    connect(button,&QPushButton::clicked,this,&Widget::close);
}

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

其他代码就用QT自动生成的不变。

click

是一个槽函数,用于编程方式触发按钮点击,调用一下相当于点击一下按钮。

clicked

是一个信号,当按钮被实际点击时发出,相当于按钮已经被触发了。

close

close是QWidget内置的槽函数,Widget继承了QWidet的槽函数;调用close可以关闭窗口。

运行

运行之后出现一下界面:

点击button之后,界面销毁。

connect的要求

信号的类型必须是发送信号的控件的。这里的button类型是QPushbutton *那么第二个参数必须是QPushButton内置的信号(或者从父类继承的信号也行)。

QT提供了哪些信号与槽

qt中有非常多的槽函数,我们不可能全部记得,需要时在文档查询即可。

在文档中查询时找不到时,要找的可能在父类中。

close这个槽就在QWidget当中:

不同版本的类型处理

cpp 复制代码
connect(const QObject *sender,//哪个控件发的信号
 const char *signal,//信号的类型
 const QObject * receiver ,//接收信号的对象,普通槽函数必须写,Lambda表达式不用写
 const char *method, //怎么处理信号
Qt::ConnectionType type = Qt::AutoConnection) const//第四个参数后面学到在解释
cpp 复制代码
    connect(button,&QPushButton::clicked,this,&Widget::close);

仔细观察connect函数的声明我们会发现,我们传入的&QPushButton::clicked和&Widget::close的类型是函数指针,并不是声明中的const char *。

旧版本
cpp 复制代码
connect(button, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close));

这是旧版 Qt 中 connect 函数的声明方式。在早期版本中,参数传递方式与现在有所不同:信号参数需要使用 SIGNAL 宏进行包装,槽函数参数则需要使用 SLOT 宏。此时,传入的函数指针会被转换为 char* 类型。

QT5以后

从 Qt 5 开始,这种写法得到了简化。不再需要使用 SIGNAL 和 SLOT 宏,connect 方法新增了重载版本。在重载版本中,第二个和第四个参数改为泛型参数,允许我们传入任意类型的函数指针。

connect函数具备参数校验功能,当参数不匹配时会触发错误提示。

在Qt5中,connect通过模板类型推导在编译阶段执行以下检查:

  1. 验证发送者是否为QObject派生类(确保支持信号槽机制);(只有QObject的派生类才支持信号与槽绑定)

  2. 检查信号函数签名合法性(确认是signals区域声明且参数类型匹配);

  3. 校验槽函数签名(确保参数类型和数量与信号兼容);

  4. 自动识别槽函数类型(普通成员函数、lambda表达式或函数指针),并适配相应的调用逻辑。

自定义槽函数

在QT5之前,自定义槽函数需要声明在类的public slots/protected slots/private slots区域中,并且必须配合SLOT宏使用。而QT5版本之后,自定义槽函数与普通成员函数已完全等同。接下来我们来自己定义槽函数;可以通过图形化的方式也可以纯代码。

纯代码

widget.h:

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 handleclicked();
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

选中handleclicked然后alt+enter就可以在.cpp文件中添加定义了。

widget.cpp:

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(400,300);




    connect(button,&QPushButton::clicked,this,&Widget::handleclicked);




}

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

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

运行:

点击按钮

图形化

在UI中拖出button,右键button,点击转到槽。这个窗口提供QPushbutton的所有信号,以及QPushbutton父类的信号。

widget.cpp

QT会自动生成槽函数

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

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

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

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

运行结果:

按下按钮,槽函数自动调用:

我们发现,图形化的方式并没有使用connect函数将信号与槽关联起来,这是为什么呢?QT中不仅可以通过connect关联信号与槽函数,还可以通过函数名的方式自动连接。

注意on_pushButton_clicked()这个生成的函数名on是固定前缀,pushButton是类的名字clicked是信号类型,只要函数名符合规则就能自动关联信号与槽。pushButton和clicked一起就确定了信号,槽函数就是我们正在写的on_pushButton_clicked(),QT会把它们自动关联。

相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00614 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术14 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript