Qt的信号与槽


信号与槽:

信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式(发布-订阅模式)。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

那信号的本质是啥呢?

信号是由用户的操作而产生的特定的事件,QT会发出某个信号,从而产生一系列的反应。

所以信号的本质是事件,当我们点击或者刷新等都会产生一个信号。

那信号是什么形式给反应的对象的呢?

我们对窗口进行操作,窗口的事件触发,发出一个特定的信号,信号的表现方式是函数,也就是我们触发信号就会调用特定的函数,通知反应的对象。

那槽的本质是啥呢?

槽就是对信号的一个处理函数。槽就是一个函数,可以有多种形式的函数:有普通函数还有类的成员函数,还可以有参数,可以被重载,最主要的是它可以是lambda表达式,但是它可以信号触发的时候被自动调用。

那信号和槽怎么连接在一起的呢?

Qt中的信号与槽函数本来是俩个不相干的东西,但是我们可以通过一个QObject::connect()函数实现连接。

[static] QMetaObject::Connection  QObject::connect(
    const QObject *sender,                                 
    const char *signal,                                 
    const QObject *receiver,                             
    const char *method,                                 
    Qt::ConnectionType type = Qt::AutoConnection

参数:

  • sender: 发出信号的对象

  • signal: sender对象的信号,信号是一个函数

  • receiver: 信号接收者

  • method: receiver对象的槽函数, 当检测到sender发出了signal信号, receiver对象调用method方法

    ​ connect函数相对于做了信号处理动作的注册,调用conenct连接信号与槽时,sender对象的信号并没有产生, 因此receiver对象的method也不会被调用,method槽函数本质是一个回调函数, 调用的时机是信号产生之后。 调用槽函数是Qt框架来执行的,connect中的sender和recever两个指针必须被实例化了, 否则conenct不会成功`

Qt中有标准的信号与槽:

系统自带的信号和槽通常如何查找呢,这个就需要利用帮助文档了,在帮助文档中比如我们上面的按钮的点击信号,在帮助文档中输入QPushButton,首先我们可以在Contents中寻找关键字 signals,信号的意思,但是我们发现并没有找到,这时候我们应该看当前类从父类继承下来了哪些信号,因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个

1648224064753

功能实现: 点击窗口上的按钮, 关闭窗口

  • 按钮: 信号发出者 -> QPushButton
  • 窗口: 信号的接收者和处理者 -> QWidget
// 单击按钮发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭窗口的槽函数
[slot] bool QWidget::close();

// 单击按钮关闭窗口
connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);

3. 自定义信号槽使用

Qt框架提供的信号槽在某些特定场景下是无法满足我们的项目需求的,因此我们还设计自己需要的的信号和槽,同样还是使用connect()对自定义的信号槽进行连接。

如果想要使用自定义的信号和槽, 首先要编写新的类并且让其继承Qt的某些标准类,我们自己编写的类想要在Qt中使用使用信号槽机制, 那么必须要满足的如下条件:

  • 这个类必须从QObject类或者是其子类进行派生
  • 在定义类的第一行头文件中加入 Q_OBJECT 宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:
class MyMainWindow : public QWidget
{
    Q_OBJECT
public:
    ......
}

3.1 自定义信号

  • 信号是类的成员函数

  • 返回值是 void 类型

  • 参数可以随意指定, 信号也支持重载

  • 信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字

  • 信号函数只需要声明, 不需要定义(没有函数体实现)

  • 在程序中发送自定义信号: 发送信号的本质就是调用信号函数

    emit mysignals();    //发送信号
    

    emit是一个空宏,没有特殊含义,仅用来表示这个语句是发射一个信号,不写当然可以。

// 举例: 信号重载
// Qt中的类想要使用信号槽机制必须要从QObject类派生(直接或间接派生都可以)
class MyButton : public QPushButton
{
    Q_OBJECT
signals:
    void testsignal();
    void testsignal(int a);
};
//qRegisterMetaType

信号参数的作用是数据传递, 谁调用信号函数谁就指定实参,实参最终会被传递给槽函数

3.2 自定义槽

槽函数就是信号的处理动作,自定义槽函数和自定义的普通函数写法是一样的。

特点:

  • 返回值是 void 类型

  • 槽函数也支持重载

    • 槽函数参数个数, 需要看连接的信号的参数个数
    • 槽函数的参数是用来接收信号发送的数据的, 信号的参数就是需要发送的数据
    • 举例:
      • 信号函数: void testsig(int a, double b);
      • 槽函数: void testslot(int a, double b);
    • 总结:
      • 槽函数的参数应该和对应的信号的参数个数, 类型一一对应
      • 信号的参数可以大于等于槽函数的参数个数,未被槽函数接受的数据会被忽略
        • 信号函数: void testsig(int a, double b);
        • 槽函数: void testslot(int a);

槽函数的类型:

  • 成员函数
    • 普通成员函数
    • 静态成员函数
  • 全局函数
  • lambda表达式(匿名函数)
  1. 槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写)
    • public slots:
    • private slots:
    • protected slots:

还有信号与槽的拓展没写。


文章作者: hehe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hehe !
  目录
​ ​