一、信号和槽机制
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。
1、连接函数
QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
//QT4版本 缺点类型不做检测
QObject::connect(scrollBar, SIGNAL(valueChanged(int value)),
label, SLOT(setNum(int value)));
- sender:发出信号的对象
- signal:发出的信号
- receiver:接收信号的对象
- method:接收对象在接收到信号之后所需要调用的函数(槽函数)
2、示例
//关闭当前窗口的按钮
QPushButton *btn = new QPushButoon("close",this);
connect(btn, &QPushButton::clicked, this, &QWidget::close);
clicked()是系统自带的信号,即鼠标点击。此外还有pressed(),鼠标按下,released()鼠标释放等。
3、自定义信号和槽
使用connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。
-
自定义信号
写到signals下,返回值为void。需要声明不需要实现,可以有参数,可以重载。示例如下:
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr){};
signals:
void Hungry();//自定义的信号函数
public slots:
};
-
自定义槽函数
写到 public slot下 或者public 或者全局函数(需要高版本)。返回值为void。需要声明且需要实现,可以有参数,可以重载。示例如下:
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr){};
signals:
public slots:
void Treat();//自定义的槽函数
};
void Student::Treat()
{
qDebug()<< "请老师吃饭";
}
-
触发自定义信号
使用 emit 在恰当的位置发送信号。
{
Teacher *tc;
Student *st;
//创建老师和学生对象
tc = new(std::nothrow) Teacher(this);
st = new(std::nothrow) Student(this);
//老师饿了 学生请客 连接
connect(tc, &Teacher::Hungry, st, &Student::Treat);
//下课函数
classIsOver();
}
void Widget::classIsOver()
{//下课后函数 触发调用老师饿了信号
emit tc->Hungry();//触发信号
}
-
当自定义信号和槽出现重载
需要利用函数指针,明确指向函数的地址。
//Teacher
void Hungry(QString foodName);
//Student
void Student::Treat(QString foodName)
{
qDebug()<<"请老师吃饭,吃"<<foodName.toUtf8().data();
}
//连接带参数的信号和槽
void(Teacher:: *teacherSignal)(QString) = &Teacher::Hungry;
void(Student:: *studentSlot) (QString) = &Student::Treat;
connect(tc,teacherSignal,st,studentSlot);
classIsOver();
- 注
- 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
- 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
- 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致
- 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。
4、信号槽的拓展
一个信号可以和多个槽相连 如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
多个信号可以连接到一个槽 只要任意一个信号发出,这个槽就会被调用。
一个信号可以连接到另外的一个信号 当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
//点击下课按钮之后在下课 --信号连接信号
QPushButton* btn = new(std::nothrow) QPushButton("下课",this);
btn->resize(60,50);
btn->move(270,400);
//点击按钮下课
// connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);
//连接无参数的信号和槽
void(Teacher:: *teacherSignal2)(void) = &Teacher::Hungry;
void(Student:: *studentSlot2) (void) = &Student::Treat;
connect(tc,teacherSignal2,st,studentSlot2);
//信号连接信号
connect(btn,&QPushButton::clicked,tc,teacherSignal2);
槽可以被取消链接 这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
信号槽可以断开 利用disconnect关键字是可以断开信号槽的
//断开信号
disconnect(tc,teacherSignal2,st,studentSlot2);
- 使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。
5、总结