信号槽基本概念
信号
当对象改变其状态时,信号就由改对象发射(emit)出去,而且对象只负责发送信号,它不知道另一端是谁在接收这个信号。
槽
用于接收信号,而且槽只是普通的对象成员函数,一个槽并不知道是否有任何信号与自己相连。
信号与槽的连接
1、标准方式(QT5之前)
[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
- 注意事项:
将信号槽的函数名称按照相应规则组合成字符串,通过connect解析该字符串,分别获取信号槽的绝对索引,然后将信号槽的链接关系,添加到信号槽容器中。
所有信号槽检查都在,运行时解析字符串的方式进行,即使字符串拼写错误也可以编译成功,创建的是空连接,在运行时报错。 - 特性:
不可以调用普通成员函数;
支持使用重载信号和槽函数。 - 链接类型:
默认:Qt::AutoConnection,可手动设置。 - 内部实现:
QObject::connect。
—·首选计算信号和槽的绝对索引;
—·将其组合成相应的存储链接,存储到信号所对应的私有元对象中的信号槽存储容器中的指定位置;
—·完成存储,并发送完成状态。
2、函数指针(QT5后方式)
[static] QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection);
- 注意事项:
编译时检查,使用函数指针作为信号槽函数。 - 特性:
可以调用普通成员函数、槽函数;
不能定义两个重载信号函数,否则无法识别
解决方案:
1、static_cast<void(QComboBox::*)(int)>(&QComboBox::activated)
2、QOverload::of(&QComboBox::activated)。 - 链接类型:
默认:Qt::AutoConnection,可手动设置。 - 内部实现:
template<typename Fun1, typename Fun2>
static inline QMetaObject::Connect connect
—·在一级调度(Object::connect)中分别获取信号和槽的数据特性,并存储到FunctionPoiter数据对象中;
—·对其数据特征进行检查;
—·检查完成,将信号和槽生成新的链接,并生成其对应的QSlotObject对象进行存储,并发送给二级调度(QObject::connctImpl)。
—·二级调度对信号和槽进行检查,获取信号的绝对索引,发送给三级调度(QObjectPrivate::connctImpl)。
—·标准流程数据操作一样。
3、Lambda表达式方式
connect(const QObject *sender, PointerToMemberFunction signal, [=](const int &arg){slot(arg)});
- 注意事项:
编译时检查,使用函数指针作为信号槽函数,lambda表达式返回的是槽函数的函数指针。 - 特性:
支持槽函数重载,不支持信号重载。
解决方案:
1、static_cast<void(QComboBox::*)(int)>(&QComboBox::activated)
2、QOverload::of(&QComboBox::activated)。 - 链接类型:
默认:Qt::DirectConnection,不可手动设置。 - 内部实现:
template<typename Fun1, typename Fun2>
static inline typename std::enable_if<QtPrivate::FunctionPointer::ArgumentCount==-1, QMetaObject::connection>::type
connect
—·与函数指针方式基本类似,唯一不同的就是连接方式指定为Qt::DirectConnection;存储槽函数的对象为QFunctorSlotObject对象。
Qt::ConnectionType常用值:
- Qt::AutoConnection(自动连接):(默认值)如果接收器位于发出信号的线程中,则使用Qt::DirectConnection。否则,将使用Qt::QueuedConnection。连接类型是在发出信号时确定的。
- Qt::DirectConnection(直接连接):在发出信号时会立即调用插槽。插槽在信号线程中执行。
- Qt::QueuedConnection(队列连接):当控制返回到接收器线程的事件循环时,将调用slot。插槽在接收器的线程中执行。
- Qt::UniqueConnection(独立连接):防止重复连接,如果当前信号和槽已经连接过滤,则不需要再连接。
- Qt::BlockingQueuedConnection(阻塞队列连接):同Qt::QueuedConnection类似,但是发送消息后阻塞,等到slot执行完。
MOC
the Meta Object Compiler。QT程序在交由标准编译器(eg:MSVC)编译之前,先使用moc分析cpp头文件;如果它发现在一个头文件中包含了Q_OBJECT宏,则会生成另外一个cpp源文件(moc_文件名.cpp),改cpp源文件包含了Q_OBJECT宏的实现、运行时信息(反射)等。
因此,QT程序的完成编译过称为moc->预编译->编译->汇编->链接->生成exe。
QT宏定义
信号槽相关的宏均定义在qobjectdefs.h文件中(位于QtCore目录)。
-
signals
#define signals public
则signals就相当于public -
slots
#define slots
则slots就是空 -
Q_OBJECT
#define Q_OBJECT \
public: \
QT_WARNING_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \
private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
struct QPrivateSignal {}; \
QT_ANNOTATE_CLASS(qt_qobject, "")
则Q_OBJECT就相当于定义了一系列成员函数和成员变量
-
emit
#define emit
则emit就是空,无作用,只是比较友好的提示。 -
SLOT&SIGNAL
#ifndef QT_NO_META_MACROS
#ifndef QT_NO_DEBUG
# define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
# ifndef QT_NO_KEYWORDS
# define METHOD(a) qFlagLocation("0"#a QLOCATION)
# endif
# define SLOT(a) qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
#else
# ifndef QT_NO_KEYWORDS
# define METHOD(a) "0"#a
# endif
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
#endif
则SLOT相当于“1”#槽函数名
SIGNAL相当于“2”#信号名
所以,预处理之后的代码,就是将这些宏直接替换掉。
QT原对象系统(The Meta-Object System)
- QT的原对象系统负责用于对象间通信的信号槽机制、运行时类型信息和属性系统(信号表、槽表、类信息表、属性表、指向父类的指针)。
- 每个QObject子类都有一个唯一的QMetaObject实例,保存了这个QObject子类的所有元信息。
- 可以通过metaObject()获取QMetaObject对象指针。
struct { // private data
SuperData superdata; // 父类QMetaObject实例的指针
const QByteArrayData *stringdata; // 字符串内存,包含MetaObject信息的字符串信息
const uint *data; // 包含MetaObject信息的索引表
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); // 回调函数
StaticMetacallFunction static_metacall; // 回调函数
const SuperData *relatedMetaObjects;
void *extradata; //reserved for future use
} d;