Qt信号槽

信号

当一个对象的内部状态发生改变时,如果其它对象对它的状态需要有所反应,这时就应该让这个类发出状态改变的信号。信号和槽其实是观察者模式的一种实现

声明信号使用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);

由此,我们总结信号槽自动关联规则如下:

  1. 使用QObject::setObjectName()方法为对象设置名称。
  2. 调用QMetaObject::connectSlotsByName()启用自动关联。
  3. 用下划线"_"将"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));
    }
}

信号槽的调用流程

好,通过以上的代码和分享我们来总结一下具体流程。

  1. moc查找头文件中的signals,slots,标记出信号和槽
  2. 将信号槽信息存储到类静态变量staticMetaObject中,并且按声明顺序进行存放,建立索引。
  3. 当发现有connect连接时,将信号槽的索引信息放到一个map中,彼此配对。
  4. 当调用emit时,调用信号函数,并且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数
  5. 通过active函数找到在map中找到所有与信号对应的槽索引
  6. 根据槽索引找到槽函数,执行槽函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值