QT信号槽
学习和使用QT,信号槽是必须掌握的一个知识点。
概念
signal,信号,扮演生产者/触发器这样的角色
slot,槽,扮演消费者/响应器这样的角色
槽作为信号的观察者而存在,在设计结构上属于“观察者模式”
关系
- 通过QObject::connect函数建立一个信号和一个槽的连接关系;
- 同一个信号可以绑定给任意个槽(0, 1, 2, …),信号触发后,所有绑定的槽会按照connect执行顺序依次被调用触发;
特征
- 使用"emit signalFunction();"这样的方式“发射信号”,发射信号的操作是无阻塞的,类似于Windows的PostMessage;
- 信号函数只需要声明,不需要实现——其作用主要是作为具体某种信号的名称(class_function)
- 信号函数和槽函数的参数必须保持一致,信号函数的参数列表会传递给槽函数
连接函数
我们直接看qobject.h的源代码
// Connect a signal to a pointer to qobject member function
// connect: 创建信号和槽的绑定关系的函数
// [参数] sender 信号类对象指针
// [参数] signal 信号函数--信号类的成员函数,带signals宏声明
// [参数] receiver 槽类对象指针
// [参数] slot 槽函数--槽类的成员函数,带slot宏声明
template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection)
{
typedef QtPrivate::FunctionPointer<Func1> SignalType;
typedef QtPrivate::FunctionPointer<Func2> SlotType;
// 断言:signal函数,所属的class里面必须有Q_OBJECT宏
static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
"No Q_OBJECT in the class with the signal");
//compilation error if the arguments does not match.
// 断言:signal函数的参数数量 >= slot函数的参数数量
static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
"The slot requires more arguments than the signal provides.");
// 断言:signal函数和slot函数的参数类型必须按顺序保持一致
static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
"Signal and slot arguments are not compatible.");
// 断言:signal函数和slot函数的返回值类型必须相同
static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
"Return type of the slot is not compatible with the return type of the signal.");
const int *types = nullptr;
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
return connectImpl(sender, reinterpret_cast<void **>(&signal),
receiver, reinterpret_cast<void **>(&slot),
new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
typename SignalType::ReturnType>(slot),
type, types, &SignalType::Object::staticMetaObject);
}