1.介绍
信号和槽用于对象之间的通信。信号和插槽机制是Qt的核心特性,也是与其他框架提供的特性最大的不同之处。
QT的信号槽被用来解决:
- 不同widget,不同对象之间的通信问题
- 回调函数带来的类型安全问题
- 回调函数与处理函数强耦合的问题
2.信号与槽
QT中使用信号槽机制代替回调函数,当特定的事件发生,信号(signal)就会被发出(emitted),而槽(Slot)是一个在接收到特定信号后调用的函数。下图展示了对象间信号槽通信的机理。
从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函数,slider有valueChanged(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();
}
}