信号与槽
信号与槽是Qt在C++的基础上新增的特性,类似于Java、C#回调的机制,但是使用起来更加方便,可以很容易做出不同组件之间的函数被动调用,是一种通信的机制。
信号是一种函数
槽也是一种函数
要使用信号函数与槽函数,有两个先决条件:
-
通信的对象必须是从QObject派生出来的
-
类中必须要加入O_OBJECT宏
函数原型
QObject::connect(const QObject * sender,
const char * signal,
const QObject * receiver,
const char * method) [static]
参数一:发射者,是信号函数的调用对象
参数二:信号函数,是在某些条件下系统自动触发的函数,在外面套上SIGNAL()
参数三:接收者,是执行槽函数的对象。
参数四:槽函数,当信号发射时要一并执行的函数,在外面套上SLOT()
接收者绑定了发射者的信号,一旦发射者发出此信号,接收者就自动执行槽函数。
信号槽除了可以连接之外,也可以断开连接,断开连接后之前连接的信号槽就不生效了。断开连接常见的方式有两种。
-
自动断连
当发射者或接收者对象销毁时,信号槽也会自动断开连接。
-
手动断连
可以使用disconnect函数断开之前连接的信号槽,其参数与connect一样。
三种绑定方式
下面依次通过三种信号槽绑定的方式来进行讲解。
1. 自带信号→自带槽
无论信号函数,还是槽函数,都不需要自己编写,只需要查阅文档找出即可。
【例子】点击按钮,关闭窗口。
发射者:按钮对象
信号函数:clicked
接收者:窗口对象
槽函数:close
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private:
QPushButton *btn;
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
btn = new QPushButton("关闭",this);
btn->move(200,200);
// 点击按钮,关闭窗口
connect(btn,SIGNAL(clicked()),this,SLOT(close()));
// 断开连接
// disconnect(btn,SIGNAL(clicked()),this,SLOT(close()));
}
Dialog::~Dialog()
{
delete btn;
}
2. 自带信号→自定义槽函数
信号函数需要查阅文档找出,槽函数需要自己编写。
槽函数是一种特殊的成员函数,只需要在声明的时候加上slots关键字即可。
【例子】点击按钮,移动窗口并打印出窗口当前的坐标。
发射者:按钮对象
信号函数:clicked
接收者:自定义窗口类对象
槽函数:自定义槽函数
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
#include <QDebug>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private:
QPushButton *btn;
// 私有槽函数声明
private slots:
void mySlot();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(200,200);
btn = new QPushButton("触发",this);
btn->move(100,100);
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
}
void Dialog::mySlot()
{
//先获得当前的坐标
int x = this->x();
int y = this->y();
//右下方
x += 10;
y += 10;
// 移动
move(x,y);
qDebug() << x << "," << y;
}
Dialog::~Dialog()
{
delete btn;
}
3. 自定义信号→自定义槽函数/自带槽函数
信号函数需要自己编写,槽函数自己写和查阅都可以。
注意,信号函数是没有权限的,且只有声明没有定义。
【例子】点击按钮,触发自定义槽函数1,在自定义槽函数1中发射自定义信号;使用自定义信号来触发自定义槽函数2。
点击按钮 → 槽函数1(发射自定义信号)
自定义信号 → 槽函数2
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
#include <QDebug>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private:
QPushButton *btn;
private slots:
// 槽函数1
void mySlot1();
void mySlot2();
// 自定义信号函数
signals:
void mySignal();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
btn = new QPushButton("发射自定义信号",this);
btn->move(200,200);
// 点击按钮,发射自定义信号
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot1()));
connect(this,SIGNAL(mySignal()),this,SLOT(mySlot2()));
}
void Dialog::mySlot1()
{
// 发射自定义信号
emit mySignal();
}
void Dialog::mySlot2()
{
qDebug() << "收到了自定义信号!";
}
Dialog::~Dialog()
{
delete btn;
}
信号槽传参
信号槽除了基础的通信功能外,还可以携带数据进行传递。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private:
QPushButton *btn;
private slots:
// 自定义槽函数1
void mySlot1();
// 自定义槽函数2:接收int参数
void mySlot2(int);
signals:
// 参数是携带的数据,可以发射给槽函数
void mySignal(int);
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(400,400);
btn = new QPushButton("0",this);
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot1()));
connect(this,SIGNAL(mySignal(int)),this,SLOT(mySlot2(int)));
}
void Dialog::mySlot1()
{
// 计数
static int count = 0;
count++;
// 把次数通过信号函数发射冲出去
emit mySignal(count);
}
void Dialog::mySlot2(int count)
{
// QString QString::number(int n, int base = 10) [static]
QString text = QString::number(count);
// 把次数显示在窗口标题上
setWindowTitle(text);
}
Dialog::~Dialog()
{
delete btn;
}
注意事项:
1. 可以传递任意多个参数
2. 参数类型必须匹配
3. 信号的参数个数可以大于等于槽函数的参数个数
上述代码如果按照正常的开发写法,可以优化为:
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private:
QPushButton *btn;
int count = 0;
private slots:
void mySlot();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
resize(300,300);
btn = new QPushButton("0",this);
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
}
void Dialog::mySlot()
{
setWindowTitle(QString::number(++count));
}
Dialog::~Dialog()
{
delete btn;
}