信号与槽机制原理

connect(sender, SIGNAL(signal), receiver, SLOT(slot));

//其中 sender 与 receiver 是指向对象的指针,SIGNAL() 与 SLOT() 是转换信号与槽的宏。  

QT4信号与槽 使用宏

在Qt 4的版本中,主要通过connect + 宏的方式进行通信连接。

connect(发送对象,信号,接收对象,槽函数) 其中发送信号和槽函数需要用 SIGNAL() 和 SLOT() 来进行声明。

connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));

自定义槽函数,需要将槽函数的声明添加到类的slots中

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

//自定义槽函数,需要将槽函数的声明添加到类的slots中
private slots:
    void closeWindow();
};

#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    //qt4 使用宏 SIGNAL  SLOT
    //connect(ui->pushButton, SIGNAL(clicked()),this,SLOT(close()));
    
    //自定义槽函数
    connect(ui->pushButton, SIGNAL(clicked()),this,SLOT(closeWindow()));
}

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

void MainWindow::closeWindow()
{
    this->close();
}

QT4使用宏好处是信号和槽参数很直观,但缺点是因为使用宏,编译时不做类型检查,如果有问题的话,在运行的时候才会发现。

QT5信号与槽

Qt 5 增加了新的 connect 函数,不需要使用 SIGNAL() 和 SLOT() 宏(但是也兼容QT4中宏的方式),可以在编译时做类型检查:

  • 使用 connect 将信号与槽函数连接,不需要再使用 SIGNAL() 和 SLOT() 宏

    //qt5 信号与槽 常规方式
    connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::close);
  • 自定义槽函数
    使用qt5这种方法槽函数, 在自定义槽函数时声明不需要放到slots中,和普通函数一样申明即可

    //qt5 自定义槽函数
    class MainWindow : public QMainWindow
    {
      Q_OBJECT
    
    public:
      explicit MainWindow(QWidget *parent = 0);
      ~MainWindow();
    
    private:
      Ui::MainWindow *ui;
    
      void closeWindow();
    };
    
    
    connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::closeWindow);
  • 函数指针
    在Qt 5版本的connect 函数里,信号与槽函数的参数其实都是函数指针。
    当信号或槽函数有重载时,使用函数指针时要明确参数,编译器才能明确使用哪一个重载函数。

    //函数指针
    bool(MainWindow:: *buttonClickSlot)() = &MainWindow::close;
    connect(ui->pushButton, &QPushButton::clicked, this, buttonClickSlot);
    
    void(MainWindow:: *closeWindowSlot)() = &MainWindow::closeWindow;
    connect(ui->pushButton, &QPushButton::clicked, this, closeWindowSlot);
    
    void(MainWindow:: *textEditedSlot)(QString) = &MainWindow::onTextEdited;
    connect(ui->lineEdit, &QLineEdit::textEdited, this, textEditedSlot);
  • Lambda表达式
    使用Lambda表达式,就不需要在类中对槽函数做任何的声明。Lambda表达式是C++ 11新增的内容,在比较低的 Qt版本中,要注意在Pro项目文件中加入 CONFIG += C++ 11。

    //Lambda表达式
      connect(ui->pushButton,&QPushButton::clicked,this,[=](){
          this->close();
      });

Qt Creator 界面添加信号的槽函数

右键要添加槽函数的控件,选择转到槽
image.png

选择槽函数
image.png

Qt Creator会自动为创建槽函数的声明和实现

//自动申明
private slots:
    void on_pushButton_clicked();

//自动实现
void MainWindow::on_pushButton_clicked()
{
    //to do
}

使用Qt Creator 界面添加信号的槽函数就不需要自己手动编写connect函数将信号与槽函数做连接。 Qt Creator自动添加的槽函数的命名有一定的规则,通常为 on_objectname_signal 这种格式。

Qt Creator 界面添加信号的槽函数优点是方便快捷,缺点是信号与槽的连接没有connect 函数看起来直观。

自定义信号与槽

信号与槽使用注意点

一个信号可以连接多个槽函数
多个信号可以连接同一个槽函数

信号和槽通过connect 函数连接就建立了耦合关系,解除连接可以使用disconnect 函数。

Qt信号的槽的优势与劣势

  • 优势
    (1)类型安全。需要关联的信号和槽的签名必须是等同的,即信号的参数类型和参数个数同接收该信号的槽的参数类型和参数个数相同。不过,一个槽的参数个数是可以少于信号的参数个数的,但缺少的参数必须是信号参数的最后一个或几个参数。如果信号和槽的签名不符,编译器就会报错。
    (2)松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是哪个对象的哪个槽需要接收它发出的信号,它只需在适当的时间发送适当的信号就可以了,而不需要知道也不关心它的信号有没有被接收到,更不需要知道是哪个对象的哪个槽收到了信号。同样的,对象的槽也不知道是哪些信号关联了自己,而一旦关联信号和槽,Qt就保证了适合的槽得到了调用。即使关联的对象在运行时被删除,应用程序也不会崩溃。
    (3)信号和槽机制增强了对象间通信的灵活性。一个信号可以关联多个槽,也可以多个信号关联一个槽。
  • 劣势
    同回调函数相比,信号和槽机制运行速度有些慢。通过传递一个信号来调用槽函数将会比直接调用非虚函数运行速度慢10倍。原因如下:
    (1)需要定位接收信号的对象;
    (2)安全地遍历所有的关联(如一个信号关联多个槽的情况);
    (3)编组/解组传递的参数;
    (4)多线程的时候,信号可能需要排队等待。
    然而,与创建对象的new操作及删除对象的delete操作相比,信号和槽的运行代价只是他们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的。

多线程下,信号槽分别在什么线程中执行,如何控制

可以通过connect的第五个参数进行控制信号槽执行时所在的线程

  connect有几种连接方式,直接连接和队列连接、自动连接

  直接连接:信号槽在信号发出者所在的线程中执行

  队列连接:信号在信号发出者所在的线程中执行,槽函数在信号接收者所在的线程中执行

  自动连接:多线程时为队列连接函数,单线程时为直接连接函数。


Simple
10 声望4 粉丝

« 上一篇
C++ -- STL库
下一篇 »
QT -- 多线程

引用和评论

0 条评论