信号
当一个对象的内部状态发生改变时,如果其它对象对它的状态需要有所反应,这时就应该让这个类发出状态改变的信号。信号和槽其实是观察者模式的一种实现
声明信号使用signals关键字
发送信号使用emit关键字
1.所有的信号声明都是公共的,所以Qt规定不能在signals前面加public,private, protected。
2.所有的信号都没有返回值,所以返回值都用void。
3.所有的信号都不需要定义。
4.必须直接或间接继承自QOBject类,并且开头私有声明包含Q_OBJECT。
5.如果一个信号链接了多个槽,那么会等所有的槽函数执行完毕后才执行后面的代码,槽函数的执行顺序是按照它们链接时的顺序执行的。
7.发出信号使用emit关键字。
8.信号参数的个数不得少于槽参数的个数。
槽
槽其实就是普通的C++函数,它唯一特点就是能和信号链接。当和它链接的信号被发出时,这个槽就会被调用。
声明槽可以使用:public/protected/private slots:
以上是Qt4的做法,在Qt5中你也不需要使用这些声明,每个函数都可以被当作是槽函数,而且还可以使用Lambda表达式来作为槽。不过为了程序的可读性,还是推荐槽函数要声明一下。
连接信号和槽
原型1
static QMetaObject::Connection connect(
const QObject *sender, //信号发送对象指针
const char *signal, //信号函数字符串,使用SIGNAL()
const QObject *receiver, //槽函数对象指针
const char *member, //槽函数字符串,使用SLOT()
Qt::ConnectionType = Qt::AutoConnection//连接类型,一般默认即可
);
//例如
connect(pushButton, SIGNAL(clicked()), dialog, SLOT(close()));
原型2
static QMetaObject::Connection connect(
const QObject *sender, //信号发送对象指针
const QMetaMethod &signal,//信号函数地址
const QObject *receiver, //槽函数对象指针
const QMetaMethod &method,//槽函数地址
Qt::ConnectionType type = Qt::AutoConnection//连接类型,一般默认即可
);
//例如
connect(pushButton, &QPushButton::clicked, dialog, &QDialog::close);
在ui中编辑信号槽
直接在ui界面连接信号槽
通过对象名关联信号槽
继续切回到ui界面,在pushButton上右击,选择“转到槽...”,在弹出的对话框中选择toggled(bool)。此时,在头文件和源文件中已经增加了on_pushButton_toggled()函数,在函数体中输入如下代码:
void SignalsAndSlots2::on_pushButton_toggled(bool checked)
{
if (checked)
{
ui->pushButton->setText("隐藏进度条");
}
else
{
ui->pushButton->setText("显示进度条");
}
}
我们打开ui_signalsandslots2.h文件,发现又多了一行
QMetaObject::connectSlotsByName(SignalsAndSlots2);
由此,我们总结信号槽自动关联规则如下:
- 使用QObject::setObjectName()方法为对象设置名称。
- 调用QMetaObject::connectSlotsByName()启用自动关联。
- 用下划线"_"将"on",“对象名”,“信号名”连接后命名的函数,即:on_对象名_信号名(参数)
这样就可以实现信号槽的自动连接啦。
QSignalMapper
当我们想要点击一个按钮,并且想将预先定好的参数一同发送出去时,由于按钮的点击事件clicked()并没有参数,那么按照一般的做法就会先定义一个槽与clicked()信号关联,然后获取参数,再通过自定义的信号将该参数发送出去。
这个过程无疑是繁琐的,为此,Qt提供了QSignalMapper这个类来解决这个问题。同时,这个类可以连接多个按钮,匹配发送信号的对象对应的整数、字符串,窗口指针,继承于QObject的对象参数重新发送它们。
现在我们创建一个类似计算器的窗口。
新建GUI项目SignalMapperWidget,类名SignalMapperWidget,基类选择QWidget。在构造函数中添加如下代码
SignalMapperWidget::SignalMapperWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::SignalMapperWidget)
{
ui->setupUi(this);
//创建垂直布局,将垂直布局作为主布局
QVBoxLayout* vLayout = new QVBoxLayout(this);
//创建编辑框,用于显示点击按钮的文字,并且文字在右边显示
QLineEdit* edit = new QLineEdit;
edit->setAlignment(Qt::AlignRight);
vLayout->addWidget(edit);//将编辑框加入到垂直布局中
//创建信号匹配器
QSignalMapper* signalMapper = new QSignalMapper(this);
//创建0-9数字键,并都加入到网格布局中
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < 10; ++i)
{
QString txt = QString::number(i);
QPushButton *button = new QPushButton(txt);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, txt);//将按钮和要发送的字符串配对
gridLayout->addWidget(button, i / 3, i % 3);//一行显示3列
}
//连接配对信号和设置文字槽
connect(signalMapper, SIGNAL(mapped(QString)),
edit, SLOT(setText(QString)));
vLayout->addLayout(gridLayout);
resize(200, 200);
}
获取信号发送者
当多个信号连接一个槽时,有时需要判断是哪个对象发来的,那么可以调用sender()函数获取对象指针,返回为QObject指针。
QObject* sender() ;
解绑定信号槽
当我们不需要信号槽连接时,可使用disconnect()进行解绑定。其写法和connect一样,只需要将connect换成disconnect即可。
关键字
signals
# define QT_ANNOTATE_ACCESS_SPECIFIER(x)
# define Q_SIGNALS public QT_ANNOTATE_ACCESS_SPECIFIER(qt_signal)
# define signals Q_SIGNALS
如果signals被展开的话就是public,所以所有的信号都是公有的,也不需要像槽一样加public,protected,private的限定符。
slots
# define QT_ANNOTATE_ACCESS_SPECIFIER(x)
# define Q_SLOTS QT_ANNOTATE_ACCESS_SPECIFIER(qt_slot)
# define slots Q_SLOTS
slots和signals一样,只是没有了限定符,所以它可以被对象调用
emit
它的宏定义:# define emit
emit后面也没有字符串!当它被替换的时候,程序其实就是调用了sigPrint()函数,而不是真正意义上的发送一个信号,有很多初学者都是认为当emit的时候,Qt会发信号,所以才会有很多人问“当emit之后,会不会立即执行其后面的代码”。
Qt之所以使用# define emit,是因为编译器并不认识emit啊,所以把它定义成一个空的宏就可以通过编译啦。
信号槽连接方式的区别
1
Qt::AutoConnection
根据signal和slot所处的线程自动决定。
如果sender线程和receiver在同一个线程。那么这个执行是同步的。相当于DirectConnection
如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接。
如果判断信号和槽所在的线程呢?根据sender和receiver是哪个线程创建的,哪个线程创建就属于哪个线程,
也可以通过QObject :: moveToThread去改变所属的线程
qt多线程的应用中使用QObject :: moveToThread就是应用这一特性
2
Qt::DirectConnection
是直接连接。相当于直接调用slot函数。
sender_thread
{
emit signal()
}
等同于
sender_thread
{
call slot()
}
slot函数一定是在sender线程的上下文执行。
这种情况一定可以直接传递栈内的地址和new(可能被发送者删除)的数据。没有问题
3
Qt::QueuedConnection
一定是在receiver的线程上下文执行
这种情况一定 不 可以直接传递栈内的地址和new(可能被发送者删除)的数据
线程A
sender_thread
{
emti signal(argument) = some_queue.push(argument)立刻返回。
}
线程B
receiver_thread
{
argument=some_queue.pop();
slot(argument);
}
4
Qt::BlockingQueuedConnection
一定可以直接传递栈的参数和new的变量。即使new的会在emit以后立刻删除
等同于
线程A
send_thread
{
emit signal= some_queue.push(argument,some_event)
waitEvent(some_event,INFINITE)
continue
}
线程B
receiver_thread
{
argument=some_queue.pop()
call slot(argument);
setEvent(event);
}
信号和槽原理
示例程序
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
public:
Counter(QObject *parent = nullptr);
~Counter();
public:
int value() const;
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_nValue;
};
#include "Counter.h"
Counter::Counter(QObject *parent)
: QObject(parent)
{
}
Counter::~Counter()
{
}
int Counter::value() const
{
return m_nValue;
}
void Counter::setValue(int value)
{
if (value != m_nValue)
{
m_nValue = value;
emit valueChanged(value);
}
}
#include "CMainWindow.h"
#include <QtWidgets/QApplication>
#include "Counter.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CMainWindow w;
w.show();
Counter counterA;
Counter counterB;
QObject::connect(&counterA, SIGNAL(valueChanged(int)), &counterB, SLOT(setValue(int)));
counterA.setValue(10); //conterA.value() = 10 counterB.value() = 10
counterB.setValue(20); //conterA.value() = 10 counterB.value() = 20
return a.exec();
}
上面用到的宏预处理之后的结果
signals
#define signals public
slots
#define 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, "")
QT_WARNING_PUSH Q_OBJECT_NO_OVERRIDE_WARNING 编译警告宏
static const QMetaObject staticMetaObject 静态成员变量
static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); 静态成员函数
emit
# define emit
可写可不写,只是用来提示开发者,发射一个信号
SIGNAL()
#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
主要就是 define SIGNAL(a) "2"#a
SLOT()
#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
主要就是 define SLOT(a) "1"#a
查看预处理之后我们的代码
左边为预处理之后的代码
class Counter : public QObject
{
public: __pragma(warning(push)) static const QMetaObject staticMetaObject; virtual const QMetaObject *metaObject() const; virtual void *qt_metacast(const char *); virtual int qt_metacall(QMetaObject::Call, int, void **); static inline QString tr(const char *s, const char *c = nullptr, int n = -1) { return staticMetaObject.tr(s, c, n); } __declspec(deprecated) static inline QString trUtf8(const char *s, const char *c = nullptr, int n = -1) { return staticMetaObject.tr(s, c, n); } private: static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); __pragma(warning(pop)) struct QPrivateSignal {};
public:
Counter(QObject *parent = nullptr);
~Counter();
public:
int value() const;
public :
void setValue(int value);
public :
void valueChanged(int newValue);
private:
int m_nValue;
};
#line 2 "h:\\c++demo\\consoleapplication1\\qtwidgetsapplication1\\counter.cpp"
Counter::Counter(QObject *parent)
: QObject(parent)
{
}
Counter::~Counter()
{
}
int Counter::value() const
{
return m_nValue;
}
void Counter::setValue(int value)
{
if (value != m_nValue)
{
m_nValue = value;
valueChanged(value);
}
}
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CMainWindow w;
w.show();
Counter counterA;
Counter counterB;
QObject::connect(&counterA, "2""valueChanged(int)", &counterB, "1""setValue(int)");
counterA.setValue(10);
counterB.setValue(20);
return a.exec();
}
moc预编译器
Qt又额外生成了moc_Counter.cpp文件,其名称为,同名源文件前加上了moc前缀
Counter.cpp和moc_Counter.cpp作为源文件一起进行编译
moc(Meta-Object Compiler)元对象预编译器。
moc读取一个c++头文件。如果它找到包含Q_OBJECT宏的一个或多个类声明,它会生成一个包含这些类的元对象代码的c++源文件,并且以moc_作为前缀。该cpp源文件中包含了Q_OBJECT宏的实现、运行时信息(反射)等
信号和槽机制、运行时类型信息和动态属性系统需要元对象代码。
由moc生成的c++源文件必须编译并与类的实现联系起来。
通常,moc不是手工调用的,而是由构建系统自动调用的,因此它不需要程序员额外的工作。
因此Qt程序的完整编译过程是moc->预处理->编译->链接
在VS中新建的Qt类头文件右击属性,可以看到moc程序会处理这个头文件
moc_Counter.cpp预处理之后的代码
//存储类的字符串相关信息
struct qt_meta_stringdata_Counter_t {
QByteArrayData data[6];
char stringdata0[46];
};
static const qt_meta_stringdata_Counter_t qt_meta_stringdata_Counter = {
{
//这个7是类名的长度 //0是下面字符的开始位置 0编号
{ { { -1 } }, 7, 0, 0, qptrdiff(__builtin_offsetof(qt_meta_stringdata_Counter_t,stringdata0) + 0 - 0 * sizeof(QByteArrayData)) }, // "Counter" 类名
//这个12是信号名 //8是下面字符的开始位置 1编号
{ { { -1 } }, 12, 0, 0, qptrdiff(__builtin_offsetof(qt_meta_stringdata_Counter_t,stringdata0) + 8 - 1 * sizeof(QByteArrayData)) }, // "valueChanged" 成员函数(信号)名
{ { { -1 } }, 0, 0, 0, qptrdiff(__builtin_offsetof(qt_meta_stringdata_Counter_t,stringdata0) + 21 - 2 * sizeof(QByteArrayData)) }, // "" 暂时不知道是啥
{ { { -1 } }, 8, 0, 0, qptrdiff(__builtin_offsetof(qt_meta_stringdata_Counter_t,stringdata0) + 22 - 3 * sizeof(QByteArrayData)) }, // "newValue" 信号 参数名
{ { { -1 } }, 8, 0, 0, qptrdiff(__builtin_offsetof(qt_meta_stringdata_Counter_t,stringdata0) + 31 - 4 * sizeof(QByteArrayData)) }, // "setValue" 槽函数名
{ { { -1 } }, 5, 0, 0, qptrdiff(__builtin_offsetof(qt_meta_stringdata_Counter_t,stringdata0) + 40 - 5 * sizeof(QByteArrayData)) } // "value" 槽函数 参数名
},
"Counter\0valueChanged\0\0newValue\0setValue\0"
"value"
};
//存储类中函数相关的信息 这个数组其实就是 struct QMetaObjectPrivate
static const uint qt_meta_data_Counter[] = {
// content:
8, // revision //qt对应的版本
0, // classname
0, 0, // classinfo
2, 14, // methods // 2个函数 content信息长度=14
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signalCount //信号的个数
// signals: name, argc, parameters, tag, flags
1, 1, 24, 2, 0x06 /* Public */, // 第一个1函数名在 qt_meta_stringdata_CStudent_t 中的存储编号 第二个1参数个数 24表示该函数在qt_meta_data_Counter数组中的偏移量
// slots: name, argc, parameters, tag, flags
4, 1, 27, 2, 0x0a /* Public */, // 4函数名在 qt_meta_stringdata_CStudent_t 中的存储编号 1参数个数 27表示该函数在qt_meta_data_Counter数组中的偏移量
// signals: parameters
QMetaType::Void, QMetaType::Int, 3,
// slots: parameters
QMetaType::Void, QMetaType::Int, 5,
0 // eod
};
void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<Counter *>(_o);
(void)_t;
switch (_id) {
case 0: _t->valueChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
case 1: _t->setValue((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (Counter::*)(int );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Counter::valueChanged)) {
*result = 0;
return;
}
}
}
}
//staticMetaObject : 类信息的总和
const QMetaObject Counter::staticMetaObject = { {
&QObject::staticMetaObject, //父类地址
qt_meta_stringdata_Counter.data, //存储字符串信息的类
qt_meta_data_Counter, //存放基本函数信息的类
qt_static_metacall, //类的信号/槽函数调用实现
nullptr,
nullptr
} };
//判断静态/动态调用
const QMetaObject *Counter::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
//返回类名称
void *Counter::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_Counter.stringdata0))
return static_cast<void*>(this);
return QObject::qt_metacast(_clname);
}
//函数调用
//void **_a : 数组指针, 每个指针指向一个函数的地址
//_c : 函数的实现类型
//_id : 函数id, 判断是否实现 & 以何种方式实现该函数
int Counter::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QObject::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 2)
qt_static_metacall(this, _c, _id, _a);
_id -= 2;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 2)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 2;
}
return _id;
}
//信号的实现
void Counter::valueChanged(int _t1)
{
//定义槽函数的函数指针
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
//发送信号,激活槽函数
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
类函数的相关信息数组就是这个结构体
struct QMetaObjectPrivate
{
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
int flags;
int signalCount;
static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject)
{ return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); }
static int originalClone(const QMetaObject *obj, int local_method_index);
static QByteArray decodeMethodSignature(const char *signature,
QArgumentTypeArray &types);
static int indexOfSignalRelative(const QMetaObject **baseObject,
const QByteArray &name, int argc,
const QArgumentType *types);
static int indexOfSlotRelative(const QMetaObject **m,
const QByteArray &name, int argc,
const QArgumentType *types);
static int indexOfSignal(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
static int indexOfSlot(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
static int indexOfMethod(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
static int indexOfConstructor(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject *m, int signal_index);
Q_CORE_EXPORT static int signalOffset(const QMetaObject *m);
Q_CORE_EXPORT static int absoluteSignalCount(const QMetaObject *m);
Q_CORE_EXPORT static int signalIndex(const QMetaMethod &m);
static bool checkConnectArgs(int signalArgc, const QArgumentType *signalTypes,
int methodArgc, const QArgumentType *methodTypes);
static bool checkConnectArgs(const QMetaMethodPrivate *signal,
const QMetaMethodPrivate *method);
static QList<QByteArray> parameterTypeNamesFromSignature(const char *signature);
#ifndef QT_NO_QOBJECT
//defined in qobject.cpp
enum DisconnectType { DisconnectAll, DisconnectOne };
static void memberIndexes(const QObject *obj, const QMetaMethod &member,
int *signalIndex, int *methodIndex);
static QObjectPrivate::Connection *connect(const QObject *sender, int signal_index,
const QMetaObject *smeta,
const QObject *receiver, int method_index_relative,
const QMetaObject *rmeta = 0,
int type = 0, int *types = 0);
static bool disconnect(const QObject *sender, int signal_index,
const QMetaObject *smeta,
const QObject *receiver, int method_index, void **slot,
DisconnectType = DisconnectAll);
static inline bool disconnectHelper(QObjectPrivate::Connection *c,
const QObject *receiver, int method_index, void **slot,
QMutex *senderMutex, DisconnectType = DisconnectAll);
#endif
};
生成的moc文件把整个对象的信息放到了QMetaObject静态成员中
struct Q_CORE_EXPORT QMetaObject
{
class Connection;
const char *className() const;
const QMetaObject *superClass() const;
bool inherits(const QMetaObject *metaObject) const Q_DECL_NOEXCEPT;
QObject *cast(QObject *obj) const;
const QObject *cast(const QObject *obj) const;
#if !defined(QT_NO_TRANSLATION) || defined(Q_CLANG_QDOC)
QString tr(const char *s, const char *c, int n = -1) const;
#endif // QT_NO_TRANSLATION
int methodOffset() const;
int enumeratorOffset() const;
int propertyOffset() const;
int classInfoOffset() const;
int constructorCount() const;
int methodCount() const;
int enumeratorCount() const;
int propertyCount() const;
int classInfoCount() const;
int indexOfConstructor(const char *constructor) const;
int indexOfMethod(const char *method) const;
int indexOfSignal(const char *signal) const;
int indexOfSlot(const char *slot) const;
int indexOfEnumerator(const char *name) const;
int indexOfProperty(const char *name) const;
int indexOfClassInfo(const char *name) const;
QMetaMethod constructor(int index) const;
QMetaMethod method(int index) const;
QMetaEnum enumerator(int index) const;
QMetaProperty property(int index) const;
QMetaClassInfo classInfo(int index) const;
QMetaProperty userProperty() const;
static bool checkConnectArgs(const char *signal, const char *method);
static bool checkConnectArgs(const QMetaMethod &signal,
const QMetaMethod &method);
static QByteArray normalizedSignature(const char *method);
static QByteArray normalizedType(const char *type);
// internal index-based connect
static Connection connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
int type = 0, int *types = nullptr);
// internal index-based disconnect
static bool disconnect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index);
static bool disconnectOne(const QObject *sender, int signal_index,
const QObject *receiver, int method_index);
// internal slot-name based connect
static void connectSlotsByName(QObject *o);
// internal index-based signal activation
static void activate(QObject *sender, int signal_index, void **argv);
static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);
static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
static inline bool invokeMethod(QObject *obj, const char *member,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
{
return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
val4, val5, val6, val7, val8, val9);
}
static inline bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType type,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
{
return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
val3, val4, val5, val6, val7, val8, val9);
}
static inline bool invokeMethod(QObject *obj, const char *member,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
{
return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
#ifdef Q_CLANG_QDOC
template<typename Functor, typename FunctorReturnType>
static bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr);
template<typename Functor, typename FunctorReturnType>
static bool invokeMethod(QObject *context, Functor function, FunctorReturnType *ret);
#else
// invokeMethod() for member function pointer
template <typename Func>
static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
Func function,
Qt::ConnectionType type = Qt::AutoConnection,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
{
return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), type, ret);
}
template <typename Func>
static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
Func function,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
{
return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), Qt::AutoConnection, ret);
}
// invokeMethod() for function pointer (not member)
template <typename Func>
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(QObject *context, Func function,
Qt::ConnectionType type = Qt::AutoConnection,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
{
return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), type, ret);
}
template <typename Func>
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& !std::is_convertible<Func, const char*>::value
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
invokeMethod(QObject *context, Func function,
typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
{
return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), Qt::AutoConnection, ret);
}
// invokeMethod() for Functor
template <typename Func>
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
&& !std::is_convertible<Func, const char*>::value, bool>::type
invokeMethod(QObject *context, Func function,
Qt::ConnectionType type = Qt::AutoConnection, decltype(function()) *ret = nullptr)
{
return invokeMethodImpl(context,
new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::move(function)),
type,
ret);
}
template <typename Func>
static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
&& QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
&& !std::is_convertible<Func, const char*>::value, bool>::type
invokeMethod(QObject *context, Func function, typename std::result_of<Func()>::type *ret)
{
return invokeMethodImpl(context,
new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::move(function)),
Qt::AutoConnection,
ret);
}
#endif
QObject *newInstance(QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const;
enum Call {
InvokeMetaMethod,
ReadProperty,
WriteProperty,
ResetProperty,
QueryPropertyDesignable,
QueryPropertyScriptable,
QueryPropertyStored,
QueryPropertyEditable,
QueryPropertyUser,
CreateInstance,
IndexOfMethod,
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType
};
int static_metacall(Call, int, void **) const;
static int metacall(QObject *, Call, int, void **);
struct { // private data
const QMetaObject *superdata;
const QByteArrayData *stringdata;
const uint *data;
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const QMetaObject * const *relatedMetaObjects;
void *extradata; //reserved for future use
} d;
private:
static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret);
};
它可以用来做反射
它的成员数据
struct { // private data
const QMetaObject *superdata; //父类地址 遍历父类的信号等
const QByteArrayData *stringdata; //存储字符串信息的类
const uint *data; //存放基本函数信息的类
//定义函数指针
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall; //类的信号/槽函数调用实现
const QMetaObject * const *relatedMetaObjects;
void *extradata; //reserved for future use
} d;
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
函数分析
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
//参数判空
if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender->metaObject()->className() : "(null)",
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+1 : "(null)");
return QMetaObject::Connection(0);
}
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind"))
return QMetaObject::Connection(0);
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //skip code
//参数类型数据
QArgumentTypeArray signalTypes;
Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
//获取信号函数名称
QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
//根据 信号名称 参数个数 参数类型 查出信号索引 它也会去遍历父类的信号
int signal_index = QMetaObjectPrivate::indexOfSignalRelative(
&smeta, signalName, signalTypes.size(), signalTypes.constData());
if (signal_index < 0) {
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
signal = tmp_signal_name.constData() + 1;
signalTypes.clear();
signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
smeta = sender->metaObject();
signal_index = QMetaObjectPrivate::indexOfSignalRelative(
&smeta, signalName, signalTypes.size(), signalTypes.constData());
}
if (signal_index < 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return QMetaObject::Connection(0);
}
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
signal_index += QMetaObjectPrivate::signalOffset(smeta);
//signal_index 这个里面保存信号的索引
QByteArray tmp_method_name;
int membcode = extract_code(method);
if (!check_method_code(membcode, receiver, method, "connect"))
return QMetaObject::Connection(0);
const char *method_arg = method;
++method; // skip code
//取槽函数的方法名和参数数组
QArgumentTypeArray methodTypes;
QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
const QMetaObject *rmeta = receiver->metaObject();
int method_index_relative = -1;
Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
//判断接收方是一个信号还是槽
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
}
if (method_index_relative < 0) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
methodTypes.clear();
methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
// rmeta may have been modified above
rmeta = receiver->metaObject();
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
}
}
if (method_index_relative < 0) {
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return QMetaObject::Connection(0);
}
//检查参数是否一致
if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),
methodTypes.size(), methodTypes.constData())) {
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n %s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
return QMetaObject::Connection(0);
}
//队列方式检查参数是否注册
int *types = 0;
if ((type == Qt::QueuedConnection)
&& !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
return QMetaObject::Connection(0);
}
#ifndef QT_NO_DEBUG
QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
//执行关联操作
QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
return handle;
}
QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
int signal_index, const QMetaObject *smeta,
const QObject *receiver, int method_index,
const QMetaObject *rmeta, int type, int *types)
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
int method_offset = rmeta ? rmeta->methodOffset() : 0;
//版本检查
Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
//拿到静态 StaticMetaCallFunction函数指针
QObjectPrivate::StaticMetaCallFunction callFunction =
rmeta ? rmeta->d.static_metacall : 0;
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
//防止重复连接。如果当前信号和槽已经连接过了,就不再连接了
if (type & Qt::UniqueConnection) {
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
if (connectionLists && connectionLists->count() > signal_index) {
const QObjectPrivate::Connection *c2 =
(*connectionLists)[signal_index].first;
int method_index_absolute = method_index + method_offset;
while (c2) {
if (!c2->isSlotObject && c2->receiver == receiver && c2->method() == method_index_absolute)
return 0;
c2 = c2->nextConnectionList;
}
}
type &= Qt::UniqueConnection - 1;
}
//生成信号槽的对应关系 Connection
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
c->sender = s;
c->signal_index = signal_index;
c->receiver = r;
c->method_relative = method_index;
c->method_offset = method_offset;
c->connectionType = type;
c->isSlotObject = false;
c->argumentTypes.store(types);
c->nextConnectionList = 0;
c->callFunction = callFunction;
//加入发送者的信号槽连接链表中
QObjectPrivate::get(s)->addConnection(signal_index, c.data());
locker.unlock();
//检测师傅关联成功
QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
if (smethod.isValid())
s->connectNotify(smethod);
return c.take();
}
void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
//connectionLists是一个vector QVector<QObjectPrivate::ConnectionList>
if (!connectionLists)
connectionLists = new QObjectConnectionListVector();
if (signal >= connectionLists->count())
connectionLists->resize(signal + 1);
//ConnectionList是一个双向链接
ConnectionList &connectionList = (*connectionLists)[signal];
if (connectionList.last) {
connectionList.last->nextConnectionList = c;
} else {
connectionList.first = c;
}
connectionList.last = c;
cleanConnectionLists();
c->prev = &(QObjectPrivate::get(c->receiver)->senders);
c->next = *c->prev;
*c->prev = c;
if (c->next)
c->next->prev = &c->next;
if (signal < 0) {
connectedSignals[0] = connectedSignals[1] = ~0;
} else if (signal < (int)sizeof(connectedSignals) * 8) {
connectedSignals[signal >> 5] |= (1 << (signal & 0x1f));
}
}
信号槽的调用流程
好,通过以上的代码和分享我们来总结一下具体流程。
- moc查找头文件中的signals,slots,标记出信号和槽
- 将信号槽信息存储到类静态变量staticMetaObject中,并且按声明顺序进行存放,建立索引。
- 当发现有connect连接时,将信号槽的索引信息放到一个map中,彼此配对。
- 当调用emit时,调用信号函数,并且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数
- 通过active函数找到在map中找到所有与信号对应的槽索引
- 根据槽索引找到槽函数,执行槽函数。