QT —— 信号和槽(槽函数)

我们之前对QT,有了一个大致的了解,如果还有对QT这个概念还不清楚的小伙伴可以点击这里:

https://blog.csdn.net/qq_67693066/category_12625974.html

今天我们来了解一下QT当中的信号和槽,这个主要就是和connect函数有关系:

信号和槽

其实我们之前已经简单使用过了信号和槽的功能,就是我们用其他方式实现HelloWorld的时候:
在这里插入图片描述
connect函数是最好的例子,这里的connect函数的意思是:mybutton会发出&QPushButton中的clicked信号,然后这个信号会被this(也就是Widget接收),接收之后,处理方法为handler

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    mybutton = new QPushButton(this); //创建一个按钮
    mybutton->move(200,300); //修改按钮位置
    mybutton->setText("这是一个按钮"); 

    connect(mybutton,&QPushButton::clicked,this,&Widget::handler);
}

void Widget::handler()
{
    if(mybutton->text() == "这是一个按钮")
    {
        mybutton->setText("你点击了按钮");
    }
    else if(mybutton->text() == "你点击了按钮")
    {
        mybutton->setText("这是一个按钮");
    }
}

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

其实可以把connect函数里面的参数可以分类一下:

在这里插入图片描述

这样的话我们就一目了然了,下面我们来介绍一下信号和槽的概念:

信号和槽(Signals & Slots)是Qt框架的核心特性,用于对象间的通信。它是一种强大的观察者模式实现,比传统的回调函数更灵活、更安全。

信号(Signal)

  • 由对象在特定事件发生时发出的通知
  • 只需声明,不需要实现(由moc自动生成)
  • 可以带有参数,用于传递数据

槽(Slot)

  • 响应特定信号的函数
  • 需要实现,可以是普通成员函数
  • 可以接收信号传递的参数

声明方式

class MyClass : public QObject
{
    Q_OBJECT  // 必须包含这个宏
    
public:
    explicit MyClass(QObject *parent = nullptr);
    
signals:    // 信号声明区
    void valueChanged(int newValue);
    void operationCompleted();
    
public slots:  // 槽函数声明区
    void setValue(int value);
    void doSomething();
    
private slots:
    void internalHandler();
};

工作原理

  1. 当信号被发射(emit)时,所有连接到该信号的槽函数都会被调用
  2. 一个信号可以连接多个槽
  3. 多个信号可以连接到一个槽
  4. 信号可以连接到另一个信号(形成信号链)

连接方式

1. 传统连接方式(Qt4风格)

connect(sender, SIGNAL(valueChanged(int)), receiver, SLOT(setValue(int)));

2. 新式连接方式(Qt5风格)

connect(sender, &SenderClass::valueChanged, receiver, &ReceiverClass::setValue);

区分槽函数和信号

我们在写代码的时候要注意槽函数和信号,槽函数的标识像一把梳子,而信号则有像WiFi一样的符号
在这里插入图片描述

通过QtCreator生成信号槽代码

如果我们创建按钮是在ui界面上创建的,还有一种方式可以帮助我们隐式连接,我们进入ui,创建一个按钮,右击转到槽
在这里插入图片描述
选择对应的信号:
在这里插入图片描述
点击OK,会自动生成一个函数:
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->pushButton->setText("这是一个按钮");
}

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

void Widget::on_pushButton_clicked()
{
     if(ui->pushButton->text() == "这是一个按钮")
     {
         ui->pushButton->setText("你点击了按钮");
     }
     else if(ui->pushButton->text() == "你点击了按钮")
     {
         ui->pushButton->setText("这是一个按钮");
     }
}

这样和显示用connect是一样的效果:
在这里插入图片描述
在这里插入图片描述

自动生成槽函数

⾃动⽣成槽函数的名称有⼀定的规则。槽函数的命名规则为:on_XXX_SSS,其中:

1、以"on"开头,中间使用下划线连接起来;
2、"XXX"表示的是对象名(控件的 objectName 属性)。
3、"SSS"表示的是对应的信号。
如:"on_pushButton_clicked()",pushButton代表的是对象名,clicked是对应的信号。

在Qt开发中,信号和槽的连接方式主要有两种风格:基于命名约定的自动连接和显式的connect调用。虽然Qt支持通过特定命名规则自动连接信号槽,但我更推荐开发者使用显式connect的方式,原因如下:

显式连接的优势

  1. 代码可读性更强

    • 通过connect语句可以一目了然地看到对象间的通信关系
    • 避免隐藏在命名规则中的"魔法连接",降低理解成本
  2. 编译时检查(Qt5新语法)

    • 使用新式语法connect(sender, &Sender::signal, receiver, &Receiver::slot)
    • 编译器会检查信号和槽的签名是否匹配,提前发现错误
  3. 减少隐式错误

    • 避免因拼写错误导致的连接失效
    • 防止因重构改名而破坏原有连接
  4. 灵活性更高

    • 可以方便地使用Lambda表达式
    • 支持动态连接/断开
    • 能够指定不同的连接类型(如跨线程的QueuedConnection)

命名约定自动连接的局限性

虽然Qt提供了on_对象名_信号名这种命名约定来自动连接信号槽(通过QMetaObject::connectSlotsByName),但这种方式的缺点包括:

  1. 隐式连接不够直观

    • 连接关系分散在代码各处,难以追踪
    • 新开发者可能不了解这种"魔法"行为
  2. 重构风险

    • 修改UI对象名称时容易破坏现有连接
    • 缺乏编译时检查,运行时才能发现错误
  3. 灵活性受限

    • 难以实现复杂的连接逻辑
    • 不支持Lambda表达式等现代C++特性

最佳实践建议

  1. 优先使用显式connect

    // 推荐 - Qt5新式语法
    connect(ui->pushButton, &QPushButton::clicked, 
            this, &MyWidget::handleButtonClick);
    
  2. 对于简单UI逻辑,可适度使用Lambda

    connect(ui->pushButton, &QPushButton::clicked, [this](){
        // 处理点击逻辑
    });
    
  3. 避免完全依赖命名约定的自动连接

    • 即使使用UI设计器生成的代码,也应检查确认连接关系
  4. 保持一致性

    • 在项目中统一采用显式连接风格
    • 在代码审查中检查连接方式

结论

虽然Qt框架提供了多种连接信号槽的方式,但显式使用connect函数能够带来更好的代码可维护性和更少的运行时错误。特别是在大型项目或团队协作中,显式声明对象间的通信关系远比依赖隐式命名约定更为可靠。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值