Qt的connect槽函数

一、connect()函数的不同参数形式,以及其区别、优略

除2(未知)之外,总体分为三种形式:1/3信号和槽转为字符串形参的connect函数、4/5/6/7/8信号和槽转为可调用对象的connect函数、9转到槽函数

1、将信号连接到另一个对象的成员函数(SIGNAL和SLOT宏实现)
原型:

static QMetaObject::Connection 
connect(const QObject *sender, const char *signal,
		const QObject *receiver, const char *member,
		Qt::ConnectionType = Qt::AutoConnection);

调用示例:

connect(ui->pushButton_1, SIGNAL(clicked(bool)), this, SLOT(onPushButton2Clicked()));

2、未知
原型:

static QMetaObject::Connection 
connect(const QObject *sender, const QMetaMethod &signal,
		const QObject *receiver, const QMetaMethod &method,
		Qt::ConnectionType type = Qt::AutoConnection);

调用示例:

/无

3、将信号连接到信号发送对象的成员函数
原型:

static QMetaObject::Connection
connect(const QObject *sender, const QMetaMethod &signal,
	const QObject *receiver, const QMetaMethod &method,
	Qt::ConnectionType type = Qt::AutoConnection);

调用示例:

connect(ui->pushButton_3, SIGNAL(clicked(bool)), SLOT(onPushButton3Clicked()));

说明:
内部实现为第1个

return connect(asender, asignal, this, amember, atype);

4、将信号连接到另一个对象的成员函数(模板方法,信号和槽对象的形参为可调用对象)
原型:

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);

调用示例:

connect(ui->pushButton_4, &QPushButton::clicked, this, &MainWindow::onPushButton4Clicked);

5、将信号连接到一个函数指针(非类的静态函数、非类的成员函数、非仿函数)
原型:

template <typename Func1, typename Func2>
static inline typename std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0, QMetaObject::Connection>::type
	connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot);

调用示例:

connect(ui->pushButton_5, &QPushButton::clicked, &onPushButton5Clicked);

说明:
a、内部实现为第5个

return connect(sender, signal, sender, slot, Qt::DirectConnection);

b、没有有第五参数,内部实现第五参数为Qt::DirectConnection

6、将信号连接到一个函数指针(非类的成员函数,一般为类的静态函数)
原型:

template <typename Func1, typename Func2>
static inline typename std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0 &&
	!QtPrivate::FunctionPointer<Func2>::IsPointerToMemberFunction, QMetaObject::Connection>::type
	connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
		Qt::ConnectionType type = Qt::AutoConnection);

调用示例:

connect(ui->pushButton_5, &QPushButton::clicked, this, &MainWindow::onPushButton5Clicked);

说明:
非类的成员函数

int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0 && !QtPrivate::FunctionPointer<Func2>::IsPointerToMemberFunction

7、将信号连接到一个仿函数
原型:

template <typename Func1, typename Func2>
static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::type
	connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot);

调用示例:

connect(ui->pushButton_7, &QPushButton::clicked, onPushButton7Clicked());
connect(ui->pushButton_8, &QPushButton::clicked, [=](bool check) {
		//do something
		qDebug() << "do something" << endl;
		qDebug() << "PushButton 8 Clicked" << endl;
	});

说明:
a、没有获取到模板参数Fun2的参数数量,即只有仿函数“类重载了()运算符”

QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1

b、但是不太清楚lambda表达式也重载的是该connect()函数,本以为会重载第5个
c、内部实现为第6个

return connect(sender, signal, sender, slot, Qt::DirectConnection);

d、没有有第五参数,内部实现第五参数为Qt::DirectConnection

return connect(sender, signal, sender, std::move(slot), Qt::DirectConnection);

8、将信号连接到连接到一个仿函数,用一个“context”对象定义将在哪个事件循环中执行
原型:

template <typename Func1, typename Func2>
static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::type
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
	const QObject *context, Func2 slot,
	Qt::ConnectionType type = Qt::AutoConnection);

调用示例:

//无

9、转到槽函数

on_pushButton_clicked()

说明:
从可视化界面的空间鼠标右键转到槽,选择槽函数自动生成

二、connect的信号和槽参数列关系要求
1、无论信号和槽参数列是否有默认形参,信号函数的参数个数一定多于或等于槽函数的参数个数
2、信号和槽对应匹配的参数必须能进行类型转换(double->float)或符合类型兼容规则(MainWindow->QMainWindow)

///"mainwindow.h"
signals:
	void signalFun1(MainWindow* This,double d);
private slots:
	void slotFun1(QMainWindow* This, float f);
///"mainwindow.cpp"
connect(this, &MainWindow::signalFun1, this,&MainWindow::slotFun1);

三、信号或者槽函数发送生重载怎么处理
1、1/3信号和槽转为字符串形参的connect函数(SIGNAL和SLOT宏实现)不会出现此问题,但是会连接失败
2、9转到槽函数不存在不匹配问题,直接调对应的重载版本的槽函数
3、4/5/6/7/8使用示例中信号和槽若重载,一般都会导致编译失败
a、信号重载解决方法如下:

///函数指针类型明确使用重载版本
connect(this, static_cast<void (MainWindow::*)(double)>(&MainWindow::signalFun2), this, &MainWindow::slotFun2);
///新写法
connect(this, QOverload<QString>::of(&MainWindow::signalFun2), this, &MainWindow::slotFun4);

c、槽重载解决方法如下:
和信号的方法一致

四、槽函数中获取信号发送者
QPushButton信号对应的槽函数中获取

QPushButton * QPB_sender = qobject_cast<QPushButton *>(sender());

五、connect()函数的返回值
1、连接失败返回false
2、连接成功返回QMetaObject::Connection对象

Connection的官方解释:

表示信号槽(或信号函子)连接的句柄。

它可以用来检查连接是否有效,并使用QObject::disconnect()断开连接。对于没有上下文对象的信号-函子连接,这是有选择地断开该连接的唯一方法。

由于Connection只是一个句柄,因此当Connection被破坏或重新分配时,底层的信号槽连接不受影响。

六、connect()函数第五参数
1、Qt::AutoConnection
自动连接:(默认)如果接收者和发送者在同一个线程,使用Qt::DirectConnection;否则,使用Qt::QueuedConnection。在发出信号时确定连接类型。

2、Qt::DirectConnection
直接连接:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。
多线程环境下会造成奔溃。

3、Qt::QueuedConnection
队列连接:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
参数类型没有注册等导致连接失败。

4、Qt::BlockingQueuedConnection
阻塞队列连接:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
如果接收方在发送信号的线程中,则不能使用此连接,否则应用程序将死锁。
5. Qt::UniqueConnection
唯一连接:这个flag可以通过按位或(|)与以上四个结合在一起使用。表示只有它不是一个重复连接,连接才会成功。如果之前已经有了一个链接(相同的信号连接到同一对象的同一个槽上),那么连接将会失败并将返回false。

if((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                || (c->connectionType == Qt::QueuedConnection)) {
    // 队列处理
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
    // 阻塞处理
    // 如果同线程,打印潜在死锁。
} else {
    //直接调用槽函数或回调函数
}

提示: 线程A向Gui主线程发送消息用Qt::BlockingQueuedConnection。若用Qt::AutoConnection连接,又在线程A中更新界面控件数据会导致Gui主线程显示异常,A为分离线程会发生概率性崩溃。

七、disconnect()函数作用
断开槽函数连接

八、信号和槽的特点
1、信号由Qt关键字signals限定定义在头文件中,无需程序员实现函数体,函数体由编译产生
2、信号和槽的返回值都为void
3、槽函数有函数体的实现
4、槽函数可以有public slots、private slots 和 protected slots三种访问限制,访问特点和普通函数一样。
5、槽函数也可以为虚函数

九、connect()函数实现原理

1、信号与槽的实现是借助了Qt 的元对象系统,元对象系统有一个元对象编译器,程序编译之前会有一个预处理过程,预处理将一个类/对象中的信号,槽的字符串值分别保存在一个容器中,可能是字符串或者其他的有序容器。

2、现在有两个容器,比如string_sig,保存了所有的信号的字符串值;string_slot保存了所有的槽函数的字符窜值。

3、每次调用connect()函数建立信号与槽的连接,就是将给定信号与给定的接收方及其槽函数存储在发送方对象的映射容器中,比如multimap,建立起信号与槽的一一对应关系。

4、发射信号其实就是调用信号函数(信号就是一种函数),根据该信号的索引在multimap中找到对应的槽函数,在调用槽函数即可。

5、信号与槽的本质就是函数的调用。

编译生成的moc_mainwindow.cpp文件

struct qt_meta_stringdata_MainWindow_t {
    QByteArrayData data[23];
    char stringdata0[292];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_MainWindow_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_MainWindow_t qt_meta_stringdata_MainWindow = {
    {
QT_MOC_LITERAL(0, 0, 10), // "MainWindow"
QT_MOC_LITERAL(1, 11, 10), // "signalFun1"
QT_MOC_LITERAL(2, 22, 0), // ""
QT_MOC_LITERAL(3, 23, 11), // "MainWindow*"
QT_MOC_LITERAL(4, 35, 4), // "This"
QT_MOC_LITERAL(5, 40, 1), // "d"
QT_MOC_LITERAL(6, 42, 10), // "signalFun2"
QT_MOC_LITERAL(7, 53, 4), // "qstr"
QT_MOC_LITERAL(8, 58, 10), // "signalFun3"
QT_MOC_LITERAL(9, 69, 21), // "on_pushButton_clicked"
QT_MOC_LITERAL(10, 91, 20), // "onPushButton2Clicked"
QT_MOC_LITERAL(11, 112, 20), // "onPushButton3Clicked"
QT_MOC_LITERAL(12, 133, 20), // "onPushButton4Clicked"
QT_MOC_LITERAL(13, 154, 20), // "onPushButton9Clicked"
QT_MOC_LITERAL(14, 175, 21), // "onPushButton10Clicked"
QT_MOC_LITERAL(15, 197, 21), // "onPushButton11Clicked"
QT_MOC_LITERAL(16, 219, 21), // "onPushButton12Clicked"
QT_MOC_LITERAL(17, 241, 8), // "slotFun1"
QT_MOC_LITERAL(18, 250, 12), // "QMainWindow*"
QT_MOC_LITERAL(19, 263, 1), // "f"
QT_MOC_LITERAL(20, 265, 8), // "slotFun2"
QT_MOC_LITERAL(21, 274, 8), // "slotFun3"
QT_MOC_LITERAL(22, 283, 8) // "slotFun4"

    },
    "MainWindow\0signalFun1\0\0MainWindow*\0"
    "This\0d\0signalFun2\0qstr\0signalFun3\0"
    "on_pushButton_clicked\0onPushButton2Clicked\0"
    "onPushButton3Clicked\0onPushButton4Clicked\0"
    "onPushButton9Clicked\0onPushButton10Clicked\0"
    "onPushButton11Clicked\0onPushButton12Clicked\0"
    "slotFun1\0QMainWindow*\0f\0slotFun2\0"
    "slotFun3\0slotFun4"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_MainWindow[] = {

 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
      17,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       4,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    2,   99,    2, 0x06 /* Public */,
       6,    1,  104,    2, 0x06 /* Public */,
       6,    1,  107,    2, 0x06 /* Public */,
       8,    1,  110,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       9,    0,  113,    2, 0x08 /* Private */,
      10,    0,  114,    2, 0x08 /* Private */,
      11,    0,  115,    2, 0x08 /* Private */,
      12,    0,  116,    2, 0x08 /* Private */,
      13,    0,  117,    2, 0x08 /* Private */,
      14,    0,  118,    2, 0x08 /* Private */,
      15,    0,  119,    2, 0x08 /* Private */,
      16,    0,  120,    2, 0x08 /* Private */,
      17,    2,  121,    2, 0x08 /* Private */,
      20,    1,  126,    2, 0x08 /* Private */,
      21,    1,  129,    2, 0x08 /* Private */,
      21,    1,  132,    2, 0x08 /* Private */,
      22,    1,  135,    2, 0x08 /* Private */,

 // signals: parameters
    QMetaType::Void, 0x80000000 | 3, QMetaType::Double,    4,    5,
    QMetaType::Void, QMetaType::Double,    5,
    QMetaType::Void, QMetaType::QString,    7,
    QMetaType::Void, QMetaType::QString,    7,

 // slots: parameters
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void, 0x80000000 | 18, QMetaType::Float,    4,   19,
    QMetaType::Void, QMetaType::Double,    5,
    QMetaType::Void, QMetaType::Double,    5,
    QMetaType::Void, QMetaType::QString,    7,
    QMetaType::Void, QMetaType::QString,    7,

       0        // eod
};

void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<MainWindow *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->signalFun1((*reinterpret_cast< MainWindow*(*)>(_a[1])),(*reinterpret_cast< double(*)>(_a[2]))); break;
        case 1: _t->signalFun2((*reinterpret_cast< double(*)>(_a[1]))); break;
        case 2: _t->signalFun2((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 3: _t->signalFun3((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 4: _t->on_pushButton_clicked(); break;
        case 5: _t->onPushButton2Clicked(); break;
        case 6: _t->onPushButton3Clicked(); break;
        case 7: _t->onPushButton4Clicked(); break;
        case 8: _t->onPushButton9Clicked(); break;
        case 9: _t->onPushButton10Clicked(); break;
        case 10: _t->onPushButton11Clicked(); break;
        case 11: _t->onPushButton12Clicked(); break;
        case 12: _t->slotFun1((*reinterpret_cast< QMainWindow*(*)>(_a[1])),(*reinterpret_cast< float(*)>(_a[2]))); break;
        case 13: _t->slotFun2((*reinterpret_cast< double(*)>(_a[1]))); break;
        case 14: _t->slotFun3((*reinterpret_cast< double(*)>(_a[1]))); break;
        case 15: _t->slotFun3((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 16: _t->slotFun4((*reinterpret_cast< QString(*)>(_a[1]))); break;
        default: ;
        }
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        switch (_id) {
        default: *reinterpret_cast<int*>(_a[0]) = -1; break;
        case 0:
            switch (*reinterpret_cast<int*>(_a[1])) {
            default: *reinterpret_cast<int*>(_a[0]) = -1; break;
            case 0:
                *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< MainWindow* >(); break;
            }
            break;
        case 12:
            switch (*reinterpret_cast<int*>(_a[1])) {
            default: *reinterpret_cast<int*>(_a[0]) = -1; break;
            case 0:
                *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< QMainWindow* >(); break;
            }
            break;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (MainWindow::*)(MainWindow * , double );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::signalFun1)) {
                *result = 0;
                return;
            }
        }
        {
            using _t = void (MainWindow::*)(double );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::signalFun2)) {
                *result = 1;
                return;
            }
        }
        {
            using _t = void (MainWindow::*)(QString );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::signalFun2)) {
                *result = 2;
                return;
            }
        }
        {
            using _t = void (MainWindow::*)(QString );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::signalFun3)) {
                *result = 3;
                return;
            }
        }
    }
}

QT_INIT_METAOBJECT const QMetaObject MainWindow::staticMetaObject = { {
    QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(),
    qt_meta_stringdata_MainWindow.data,
    qt_meta_data_MainWindow,
    qt_static_metacall,
    nullptr,
    nullptr
} };


const QMetaObject *MainWindow::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *MainWindow::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_MainWindow.stringdata0))
        return static_cast<void*>(this);
    return QMainWindow::qt_metacast(_clname);
}

int MainWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QMainWindow::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        if (_id < 17)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 17;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 17)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 17;
    }
    return _id;
}

参考:
Qt connect的实现原理:https://blog.csdn.net/weixin_52511809/article/details/115728244

Qt进阶之路:https://zhuanlan.zhihu.com/p/80539605

拓展1:设计模式的观察者模式
拓展2:函数回调(回调函数是否为虚函数)
拓展3:槽函数为虚函数的使用

本文测试代码下载链接:https://download.csdn.net/download/qq_43148810/55122137

如有错误或不足欢迎评论指出!创作不易,转载请注明出处。如有帮助,记得点赞关注哦(⊙o⊙)
更多内容请关注个人博客:https://blog.csdn.net/qq_43148810

你可能感兴趣的:(Qt,qt,ui,connect,槽函数,信号与槽)