QT的信号槽机制(slot与signal)

2,328 阅读4分钟

1.介绍

信号和槽用于对象之间的通信。信号和插槽机制是Qt的核心特性,也是与其他框架提供的特性最大的不同之处。

QT的信号槽被用来解决:

  • 不同widget,不同对象之间的通信问题
  • 回调函数带来的类型安全问题
  • 回调函数与处理函数强耦合的问题

2.信号与槽

QT中使用信号槽机制代替回调函数,当特定的事件发生,信号(signal)就会被发出(emitted),而槽(Slot)是一个在接收到特定信号后调用的函数。下图展示了对象间信号槽通信的机理。

信号槽解决了回调的类型安全问题,信号的签名必须与接收插槽的签名匹配。信号和插槽之间是松散耦合的:发出信号的类既不知道也不关心哪个插槽接收信号。 Qt的信号和插槽机制可确保如果将信号连接到插槽,则将在正确的时间使用信号的参数调用该插槽。

从QObject或其子类之一(例如QWidget)继承的所有类都可以包含信号和插槽。插槽可以用来接收信号,但它们也是普通的成员函数。就像对象不知道是否有东西接收到它的信号一样,槽也不知道是否有信号连接到它。这确保了可以使用Qt创建真正独立的组件。

qt的信号与槽有着如下的特点:

  • 信号可以连接到多个插槽
  • 插槽可以连接到多个信号
  • 信号可以连接到信号:这是信号中继。 如果发送第一个信号,则发送第二个信号。

3.例子1:一个关闭按钮

QPushButton提供了一个名为clicked的 signal,QApplication自带了一个quit的slot(用于关闭应用),由于需要使用connect(Object1,signal,Object2,slot),因此需要访问QApplication的实例,QApplication中自带了一个静态方法instance可以访问QApplication的实例。

代码如下:

mainwindow.cpp

#include "mainwindow.h"
#include <QPushButton>
#include<QApplication>
MainWindow::MainWindow(QWidget *parent) :
 QWidget(parent)
 {
 // Set size of the window
 setFixedSize(500, 500);

 // Create and position the button
 m_button = new QPushButton("Close", this);
 m_button->setGeometry(10, 10, 80, 30);

 // NEW : Do the connection
 connect(m_button, SIGNAL (clicked()), QApplication::instance(), SLOT (quit()));
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>

class QPushButton;
class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private:
    QPushButton *m_button;
};
#endif // MAINWINDOW_H

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

4.例子2:进度条与滑动输入框相同步

进度条QProgressBar有着名为setValue(int value)的slot函数,slidervalueChanged(int)的信号函数,将其连接起来,从而实现界面部件间数据的通信。

mainwindow.cpp

#include "mainwindow.h"
#include <QPushButton>
#include <QProgressBar>
#include <QSlider>
#include<QApplication>
MainWindow::MainWindow(QWidget *parent) :
 QWidget(parent)
 {
 // Set size of the window
 setFixedSize(500, 500);


 progressBar=new QProgressBar(this);
 progressBar->setRange(0, 100);
 progressBar->setValue(0);
 progressBar->setGeometry(10, 10, 180, 30);
 slider=new QSlider(this);
 slider->setOrientation(Qt::Horizontal);
 slider->setRange(0, 100);
  slider->setValue(0);
  slider->setGeometry(10, 40, 180, 30);

 // Connection
 // This connection set the value of the progress bar
 // while the slider's value changes
 QObject::connect(slider, SIGNAL (valueChanged(int)), progressBar, SLOT (setValue(int)));
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>

class QPushButton;
class QProgressBar;
class QSlider;
class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private:
    QPushButton *m_button,*m_button2;
    QProgressBar *progressBar;
    QSlider *slider;
};
#endif // MAINWINDOW_H

5.自定义信号槽

总的来说有五步:

  • 添加Q_OBJECT宏

  • 添加信号部分,并编写信号函数(使用emit 发射信号)

  • 添加public slot或protected slot或private slot

  • 将槽函数作为普通方法实现

  • 建立联系(调用connect函数)

5.1 自定义槽slot 点击改变按钮文字

mainwindow.h

class QPushButton;
class Window : public QWidget
{
 Q_OBJECT
public:
 explicit Window(QWidget *parent = 0);
private slots:
 void slotButtonClicked(bool checked);
private:
 QPushButton *m_button;
};

mainwindow.cpp中实现slotButtonClicked

#include "mainwindow.h"
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent) :
 QWidget(parent)
 {
 // Set size of the window
 setFixedSize(500, 500);
m_button= new QPushButton("按钮",this);
m_button->setGeometry(10, 10, 80, 30);
m_button->setCheckable(true);
connect(m_button, SIGNAL (clicked(bool)), this, SLOT (slotButtonClicked(bool)));


 // Connection
 // This connection set the value of the progress bar
 // while the slider's value changes

}
void MainWindow::slotButtonClicked(bool checked)
{
 if (checked) {
 m_button->setText("Checked");
 } else {
 m_button->setText("按钮");
 }
}

5.2 自定义信号 点击按钮十下后关闭窗口

mainwindow.h添加私有成员 m_count,同时在signals关键字下声明信号函数counterReached(),在之前定义的槽函数slotButtonClicked中统计点击次数,赋值给m_count,判断到十次时使用emit 触发信号函数counterReached(),并且在构造函数中将该信号与QApplication::instance()相连接。

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>

class QPushButton;
class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
signals:
 void counterReached();
private:
    QPushButton *m_button;
    int m_count;
private slots:
    void slotButtonClicked(bool checked);
};
#endif // MAINWINDOW_H


mainwindow.cpp

#include "mainwindow.h"
#include <QPushButton>
#include<QApplication>
MainWindow::MainWindow(QWidget *parent) :
 QWidget(parent)
 {
 // Set size of the window
 setFixedSize(500, 500);
m_button= new QPushButton("按钮",this);
m_button->setGeometry(10, 10, 80, 30);
m_button->setCheckable(true);
m_count=0;
connect(m_button, SIGNAL (clicked(bool)), this, SLOT (slotButtonClicked(bool)));
connect(this,SIGNAL(counterReached()),QApplication::instance(),SLOT(quit()));

 // Connection
 // This connection set the value of the progress bar
 // while the slider's value changes

}
void MainWindow::slotButtonClicked(bool checked)
{
 if (checked) {
 m_button->setText("Checked");
 } else {
 m_button->setText("按钮");
 }
 m_count++;
 if(m_count==10){
     emit counterReached();
 }
}