Qt信号和槽

0. 概述

在Qt中,⽤⼾和控件的每次交互过程称为⼀个事件。⽐如"⽤⼾点击按钮"是⼀个事件,"⽤⼾关闭窗⼝"也是⼀个事件。每个事件都会发出⼀个信号,例如⽤⼾点击按钮会发出"按钮被点击"的信号,⽤⼾关闭窗⼝会发出"窗⼝被关闭"的信号。

Qt中的所有控件都具有接收信号的能⼒,⼀个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗⼝接收到"按钮被点击"的信号后,会做出"关闭⾃⼰"的响应动作;再⽐如输⼊框⾃⼰接收到"输⼊框被点击"的信号后,会做出"显⽰闪烁的光标,等待⽤⼾输⼊数据"的响应动作。在Qt中,对信号做出的响应动作就称之为槽。

信号和槽是Qt特有的消息传输机制,它能将相互独⽴的控件关联起来。⽐如,"按钮"和"窗⼝"本⾝是两个独⽴的控件,点击"按钮"并不会对"窗⼝"造成任何影响。通过信号和槽机制,可以将"按钮"和"窗⼝"关联起来,实现"点击按钮会使窗⼝关闭"的效果。

1. 信号和槽的使用

在Qt中,QObject类提供了⼀个静态成员函数connect(),该函数专⻔⽤来关联指定的信号函数和槽函数。

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);
  • sender:信号的发送者,描述了当前信号是哪个控件发出来的
  • signal:发送的信号(信号函数),信号的类型
  • receiver:信号的接收者,哪个对象(控件)负责处理
  • method:接收信号的槽函数,这个对象怎么处理
  • type:⽤于指定关联⽅式,默认的关联⽅式为Qt::AutoConnection,通常不需要⼿动设定。

代码⽰例:在窗⼝中设置⼀个按钮,当点击"按钮"时关闭"窗⼝"

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);
}

注意,如果要想使用信号和槽的功能,需要在类的私有属性中加上Q_OBJECT

2. 自定义槽

2.1 规范

⾃定义槽函数书写规范
(1)早期的Qt版本要求槽函数必须写到"public slots"下,但是现在⾼级版本的Qt允许写到类的"public"作⽤域中或者全局下;
(2)返回值为void,需要声明,也需要实现;
(3)可以有参数,可以发⽣重载;

2.2 例子1,自定义槽

一点击按钮,就更换窗口的标题

// widget.h
class Widget : public QWidget
{
    Q_OBJECT

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

private:
    Ui::Widget *ui;
};

// widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* b = new QPushButton(this);
    b->setText("按钮");
    b->move(200, 200);
    connect(b, &QPushButton::clicked, this, &Widget::handlerClicked);
}

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

void Widget::handlerClicked()
{
    this->setWindowTitle("点击了按钮!");
}

image-20241027221248532

2.3 例子2,使用widget.ui

点击转到槽,注意这里右边属性中的objectName

image-20241028201901200

此时,生成了一个on_mybt1_clicked()函数

image-20241028202130882

3. 自定义信号

3.1 规范

⾃定义信号函数书写规范

(1)⾃定义信号函数必须写到"signals"下;
(2)返回值为void,只需要声明,不需要实现;这个函数的定义,是qt在编译过程中自动生成的
(3)有没有参数都可以,也可以发⽣重载;

发送信号使⽤"emit"关键字发送信号
"emit"是⼀个空的宏。"emit"其实是可选的,没有什么含义,只是为了提醒开发⼈员
Qt内置的信号,不需要手动通过代码来触发,而是用户在GUI进行某些操作,就会自动触发对应的信号(发射信号的代码已经内置到Qt框架中了)

3.2 例子1,基础使用

点击按钮后发送信号

// widget.h
class Widget : public QWidget
{
    signals:
        void mySignal();
    // 当然可以省略slots
    private slots:
        void on_pushButton_clicked();
        void handlerMySignal();
}
// widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this, &Widget::mySignal, this, &Widget::handlerMySignal);
}
void Widget::handlerMySignal()
{
    this->setWindowTitle("处理自定义信号");
}
void Widget::handlerMySignal()
{
    this->setWindowTitle("处理自定义信号");
}

void Widget::on_pushButton_clicked()
{
    // 点击按钮时发送信号
    emit mySignal();	// 省略emit也可以
}

image-20241103123425631

3.3 例子2,多对多

Qt中支持一个信号连接多个槽,也支持多个信号连接一个槽

// .h
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
signals:
    void mySig1();
    void mySig2();
    void mySig3();
private slots:
    void handlerSig1();
    void handlerSig2();
    void handlerSig3();
private:
    Ui::Widget *ui;
};
// .cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 一个信号连接多个槽
    connect(this, &Widget::mySig1, this, &Widget::handlerSig1);
    connect(this, &Widget::mySig1, this, &Widget::handlerSig2);
    connect(this, &Widget::mySig1, this, &Widget::handlerSig3);
    // 多个信号连接一个槽
    connect(this, &Widget::mySig1, this, &Widget::handlerSig1);
    connect(this, &Widget::mySig2, this, &Widget::handlerSig1);
    connect(this, &Widget::mySig3, this, &Widget::handlerSig1);
    emit mySig1();
    emit mySig2();
    emit mySig3();
}

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

void Widget::handlerSig1()
{
    qDebug() << "handlerSig1()";
}

void Widget::handlerSig2()
{
    qDebug() << "handlerSig2()";
}

void Widget::handlerSig3()
{
    qDebug() << "handlerSig3()";
}

image-20241103155408822

4. 带参数的信号和槽

4.1 规范

Qt的信号和槽也⽀持带有参数,同时也可以⽀持重载。

要求:信号函数的参数列表要和对应连接的槽函数参数列表⼀致。(这里的一致指的是类型一致,如果参数个数不一致的时候,要求信号的参数个数必须比槽的个数多)

此时若信号触发,调⽤到槽函数的时候,信号函数中的实参就能够被传递到槽函数的形参当中。

4.2 例子1,基础使用

改写3.2中的代码。写两个按钮,可以根据信号传参改变标题

// widget.h
class Widget : public QWidget
{
    signals:
        void mySignal(const QString &);
    // 当然也可以省略slots
    private slots:
        void on_pushButton_clicked();
        void on_pushButton_2_clicked();
        void handlerMySignal(const QString &);
}
// widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this, &Widget::mySignal, this, &Widget::handlerMySignal);
}

void Widget::handlerMySignal(const QString& text)
{
    this->setWindowTitle(text);
}

void Widget::on_pushButton_clicked()
{
    // 点击按钮时发送信号
    emit mySignal("设置标题1");
}

void Widget::on_pushButton_2_clicked()
{
    // 点击按钮时发送信号
    emit mySignal("设置标题2");
}

image-20241103142108768

5. 其它

5.1 信号与槽的断开

使⽤disconnect即可完成断开。
disconnect的⽤法和connect基本⼀致。

下面的例子是让一个信号先和一个槽断开连接,然后再和另一个槽建立连接

// .h
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void handlerClick1();
    void handlerClick2();
    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};

// .cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 默认是handlerClick1()
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handlerClick1);
}

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

void Widget::handlerClick1()
{
    this->setWindowTitle("标题1");
    qDebug() << "handlerClick1()";
}

void Widget::handlerClick2()
{
    this->setWindowTitle("标题2");
    qDebug() << "handlerClick2()";
}

void Widget::on_pushButton_2_clicked()
{
    // 切换连接
    disconnect(ui->pushButton, &QPushButton::clicked, this, &Widget::handlerClick1);
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handlerClick2);
}

image-20241103170513324

5.2 使用lambda 定义槽函数

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* bt = new QPushButton(this);
    bt->setText("按钮");
    bt->move(100,100);
    connect(bt, &QPushButton::clicked, this, [=](){
       this->setWindowTitle("lambda 表达式被成功执行!");
    });
}

image-20241103175030096

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值