信号和槽的概念:
类似于一些其他的编程语言或者框架中的回调机制,信号和槽是Qt基于C++语法新增的特性,使用起来非常方便,可以完成不同对象之间的通信
信号槽的使用需要有以下两个条件:
(1)通信的对象必须继承自QObject类,QObject是Qt所有内置类型的基类
(2)类中要有Q_OBJECT宏
函数原型:
信号槽需要连接后才能触发,因此信号槽的核心是连接函数,连接函数是一个静态成员函数
QObject::connect(const QObject * sender,
const char * signal,
const QObject * receiver,
const char * method) [static]
参数1:发射者,发射者是一个对象,此对象发射信号函数,作为信号槽的触发条件
参数2:信号函数,参数一中的发射者发射出的信号函数,作为信号槽的触发条件,需要使用SIGNAL()包裹函数名称
参数3:接收者,接收者是一个对象,此对象执行槽函数,作为信号槽的执行结果
参数4:槽函数,参数3中接收者要执行的槽函数,作为信号槽的执行结果。需要使用SLOT()包裹函数名称
接收者绑定了发射者的信号函数,一旦发射者发射信号函数,接收者就执行槽函数
注意:
还有第五个参数,一般使用默认的。
(1)Qt::AutoConnection:默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型
(2)Qt::DirectConnection:槽函数会在信号发送的时候直接调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能造成崩溃
(3)Qt::QueuedConnection:槽函数在控制回到接收者所在的线程的事件循环时被调用,槽函数运行于信号接收者所在的线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个
(4)Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::Queued一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个
(5)Qt::UniqueConnection:这个flag可以通过按位与(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,在进行重复连接就会失败。也就是避免了重复连接
连接方式:
例子:
用槽函数实现:点击按钮,关闭窗口
(1)自带信号->自带槽
分析:先找因果关系的四个参数
参数1:发射者,按钮对象
参数2:信号函数,点击
参数3:接收者,窗口对象
参数4:槽函数,关闭
代码示例:
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)
{
resize(400,400);
btn = new QPushButton("关闭",this);
btn->setGeometry(100,100,100,100);
// 连接信号槽
// 参数1:发射者,按钮对象
// 参数2:信号函数,点击
// 参数3:接收者,窗口对象
// 参数4:槽函数,关闭
connect(btn,SIGNAL(clicked()),this,SLOT(close()));
}
Dialog::~Dialog()
{
delete btn;
}
(2)自带信号->自定义槽
在实际开发中,Qt不可能内置所有的函数,特别是槽函数,更好地情况是需要用户自定义一个槽函数来实现特定的功能,这种方式也是最常见的一种信号槽连接方式
需要注意的是,槽函数是一种特殊的成员函数,实现槽函数的方式与普通成员函数类似
代码示例:
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(400,400);
btn = new QPushButton("向右下方移动",this);
btn->setGeometry(100,100,100,100);
// 连接信号槽
// 参数1:按钮对象 btn
// 参数2:clicked
// 参数3:窗口对象 this
// 参数4:自定义槽函数 mySlot
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
}
void Dialog::mySlot()
{
// 先获得当前窗口的坐标
int x = this->x();
int y = this->y();
// 增加后移动
move(x+10,y+10);
// 输出当前坐标
qDebug() << x+10 << "," << y+10;
}
Dialog::~Dialog()
{
delete btn;
}
(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;
private slots:
// 自定义槽函数
void mySlot();
signals: // 信号函数没有权限
// 自定义信号函数只需要声明就能使用
void mySignal();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
btn = new QPushButton("关闭",this);
btn->move(100,100);
// 第一个信号槽的连接
connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
// 第二个信号槽的连接
connect(this,SIGNAL(mySignal()),this,SLOT(close()));
}
void Dialog::mySlot()
{
// 发射自定义信号
emit mySignal();
}
Dialog::~Dialog()
{
delete btn;
}