Qt的信号槽机制不仅可以使用系统提供的部分,也可以自定义信号槽。信号槽是Qt的核心机制,只要是继承了QObject类的子类或者间接子类都可以使用信号槽机制,无论是不是GUI程序。
下面我们来实现一个自定义的信号槽,我们将有两个类,分别是Teacher和Student。老师讲课,学生听课。
//Teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QString str)
{
this->str = str;
}
void send()
{
emit speak(str); //发射信号
}
signals:
void speak(const QString & str); //信号
private:
QString str;
};
#endif // TEACHER_H
//Student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
#include <QDebug>
class Student : public QObject
{
Q_OBJECT
public:
//explicit Student(QObject *parent = nullptr);
void listen(const QString & str)
{
qDebug() << "老师讲的内容是:" << str;
}
signals:
};
#endif // STUDENT_H
//main.cpp
#include <QCoreApplication>
#include "teacher.h"
#include "student.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Teacher teacher("1+1=2");
Student studet;
QObject::connect(&teacher, &Teacher::speak, &studet, &Student::listen);
teacher.send();
return a.exec();
}
运行结果如下:
- 为了能使用信号槽机制,我们的Teacher和Student都继承自QObject。并且在类中的第一行就写上了Q_OBJECT宏。这个宏为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力。这个宏非常重要,很多操作都需要依赖这个宏。
- 在Teacher类中,我们在signals下写了个信号函数的声明。没错,我们仅仅只是写了个声明,并没有实现它。而Qt居然编译通过,成功运行。这是因为,Qt有一个叫做moc(Meta Object Compiler,元对象编译器)的工具,它帮助我们实现了函数体。其实Q_OBJECT也是被moc处理的,moc会处理含有Q_OBJECT的头文件。其实我们可以看到这个moc处理之后的文件。具体是这样的。如果你勾选了项目下的Shadow build(Qt Creator 4.11.0默认勾选),那么Qt Creator会生成影子文件在另外一个文件夹里,这里放着你的Debug或者Release版本的可执行文件以及moc处理过后moc_xxx.cpp文件。这些cpp文件就是将xxx.h文件处理之后的形成的。如果你不勾选Shadow build,那么就和代码放在一个文件夹下。Qt这么做保证了源代码的纯净,使用太久的VS,我喜欢不勾选这个选项。
- 信号函数声明必须放在signals之下,另外signals受到public,protected和private限制。如果上面代码中的是私有信号,那么我们将无法使用它。信号函数的形式被规定如下:返回值只能是void,因为无法获得信号的返回值。信号可以把想让槽函数知道的东西放在参数里。
- emit也是一个宏,它的功能就是发送信号。
- qDebug()函数是提供在控制台或者应用程序输出区进行输出的,使用它需要包含头文件QDebug.
因此,自定义信号槽需要三个步骤:
- 声明信号函数和定义槽函数
- 连接信号槽
- 发射信号
信号可以连接到信号,这和信号槽的连接没有本质区别;
一个信号可以连接多个槽函数,但是槽函数的执行顺序是不确定的,不建议这么做;
一个槽函数可以连接多个信号;
信号槽的连接可以被取消,使用disconnect函数。