所谓信号槽,实际就是观察者模式_百度百科 (baidu.com)。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。(这里提一句,Qt 的信号槽使用了额外的处理来实现 ,并不是 GoF 经典的观察者模式的实现方式。)
信号和槽是Qt特有的信息传输机制,是Qt设计程序的重要基础,它可以让互不干扰的对象建立一种联系。
槽的本质是类的成员函数,其参数可以是任意类型的。和普通C++成员函数几乎没有区别,它可以是虚函数;也可以被重载;可以是公有的、保护的、私有的、也可以被其他C++成员函数调用。唯一区别的是:槽可以与信号连接在一起,每当和槽连接的信号被发射的时候,就会调用这个槽。
信号槽是基于Object类型的,Object是Qt的核心类,
C++ ——Qt的信号和槽的详解 - 简书 (jianshu.com)
2、信号槽使用方法参考Qt的信号槽基本用法总结_hanxiaoyong_的博客-CSDN博客
3、Qt信号槽的实现任何实现信号槽或者属性的对象,都必须包含Q_OBJECT宏,且在源文件上运行Meta Object Complier(moc工具),才可生成Class_moc.cpp文件,Class_moc.cpp文件会将特定信号与特定的槽函数关联起来,后面会讲解Class_moc.cpp文件。另:在任何定义的QObject的所有子类中都应添加Q_OBJECT宏。Q_OBJECT的官方定义如下:
The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
Q_OBJECT宏必须出现在声明自己的信号和槽或使用Qt元对象系统提供的其他服务的类定义的私有部分中。注:基于Object类,且包含Q_OBJECT的宏,通过moc工具会生成Class_moc.cpp文件。
打开qobjectdefs.h头文件,可以看到Q_OBJECT宏内容如下:
#define Q_OBJECT
public:
QT_WARNING_PUSH
Q_OBJECT_NO_OVERRIDE_WARNING
static const QMetaObject staticMetaObject;
virtual const QMetaObject *metaObject() const;
virtual void *qt_metacast(const char *);
virtual int qt_metacall(QMetaObject::Call, int, void **);
QT_TR_FUNCTIONS
private:
Q_OBJECT_NO_ATTRIBUTES_WARNING
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
QT_WARNING_POP
struct QPrivateSignal {};
QT_ANNOTATE_CLASS(qt_qobject, "")
通过上述的宏定义可以看出,Q_OBJECT是宏定义函数,在程序编译时会使用上述的函数替换Q_OBJECT宏,替换后生成Class_moc.cpp文件。
上面宏中的函数:
静态对象:staticMetaOject
静态方法:qt_static_metacall
成员虚函数:metaObject,
qt_metacast,
qt_metall
现在自定义类如下(Qt5.13版本):
//.h #pragma once #include#include "ui_SignalSlot.h" #include class SignalSlot : public QWidget { Q_OBJECT public: SignalSlot(QWidget *parent = Q_NULLPTR); public slots: //响应btn1 void OnBtn1(); void OnSetTextStyle(); void OnSetWindowTitile(); //响应自定义信号 void OnSignalMsg(); //响应btn2 clicked void OnBtn2Signal(); //响应自定义信号 void OnCustomSignal(int x,int y); //自定义信号 signals: void signalMsg1(); void signalMsg2(); void signalMsg3(); void signalMsg(int x,int y); private: Ui::SignalSlotClass ui; QTimer *m_pTimer; }; //.cpp #include "SignalSlot.h" #include #include SignalSlot::SignalSlot(QWidget *parent) : QWidget(parent) { ui.setupUi(this); ///一个信号关联多个槽函数 start// connect(ui.pbtn1,SIGNAL(clicked()),this,SLOT(OnBtn1())); connect(ui.pbtn1, SIGNAL(clicked()), this, SLOT(OnSetTextStyle())); connect(ui.pbtn1, SIGNAL(clicked()), this, SLOT(OnSetWindowTitile())); /一个信号关联多个槽函数 end ///多个信号关联一个槽函数 start// connect(this, SIGNAL(signalMsg1()), this, SLOT(OnSignalMsg()())); connect(this, SIGNAL(signalMsg2()), this, SLOT(OnSignalMsg()())); connect(this, SIGNAL(signalMsg3()), this, SLOT(OnSignalMsg()())); ///多个信号关联一个槽函数 end// ///一个信号关联多个信号 start// ///一个信号关联一个信号 start// connect(this, SIGNAL(signalMsg3()), ui.pbtn2, SIGNAL(clicked())); connect(ui.pbtn2, SIGNAL(clicked()), this, SLOT(OnBtn2Signal())); ///一个信号关联一个信号 end// connect(this, SIGNAL(signalMsg3()), ui.pbtn2, SIGNAL(pressed())); connect(ui.pbtn2, &QPushButton::clicked, this,&SignalSlot::OnBtn2Signal); ///一个信号关联多个信号 end// connect(this, SIGNAL(signalMsg(int,int)), this, SLOT(OnCustomSignal(int,int))); //信号槽lambda写法 m_pTimer = new QTimer(this); connect(m_pTimer, &QTimer::timeout, [=]() { ui.lineEdit3->setText(QString::fromLocal8Bit("你好,中国")); ui.lineEdit4->setText(QString::fromLocal8Bit("你好,中国")); qDebug() << "11111" << endl; m_pTimer->stop(); }); m_pTimer->start(1000*10);//启动定时器 } void SignalSlot::OnBtn1() { ui.lineEdit1->setText(QString::fromLocal8Bit("你好,世界!")); qDebug() << "OnBtn1" < setStyleSheet(" QLineEdit{ background: yellow }"); qDebug() << "OnSetTextStyle" << endl; } void SignalSlot::OnSetWindowTitile() { this->setWindowTitle(QString::fromLocal8Bit("测试窗口")); qDebug() << "OnSetWindowTitile" << endl; emit signalMsg1(); emit signalMsg2(); emit signalMsg3(); } void SignalSlot::OnSignalMsg() { qDebug() << sender()->objectName() << endl; } void SignalSlot::OnBtn2Signal() { ui.lineEdit2->setText(QString::fromLocal8Bit("你好,中国")); qDebug() << "OnBtn2Signal"<< endl; } void SignalSlot::OnCustomSignal(int x, int y) { qDebug() << "x" << x << ",y" << y << endl; }
生成的moc.cpp文件如下
#include#include "../../SignalSlot.h" #include #include #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'SignalSlot.h' doesn't include ." #elif Q_MOC_OUTPUT_REVISION != 67 #error "This file was generated using the moc from 5.13.0. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif QT_BEGIN_MOC_NAMESPACE QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED struct qt_meta_stringdata_SignalSlot_t { QByteArrayData data[14]; //里面有14个QByteArrayData的类型元素,这些元素都是字符结 //构,如用QT_MOC_LITERAL表示的"SignalSlot",这些字符串表示了信号名、槽函数名, char stringdata0[139]; }; #define QT_MOC_LITERAL(idx, ofs, len) Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, qptrdiff(offsetof(qt_meta_stringdata_SignalSlot_t, stringdata0) + ofs - idx * sizeof(QByteArrayData)) ) static const qt_meta_stringdata_SignalSlot_t qt_meta_stringdata_SignalSlot = { { QT_MOC_LITERAL(0, 0, 10), // "SignalSlot" QT_MOC_LITERAL(1, 11, 10), // "signalMsg1" QT_MOC_LITERAL(2, 22, 0), // "" QT_MOC_LITERAL(3, 23, 10), // "signalMsg2" QT_MOC_LITERAL(4, 34, 10), // "signalMsg3" QT_MOC_LITERAL(5, 45, 9), // "signalMsg" QT_MOC_LITERAL(6, 55, 1), // "x" QT_MOC_LITERAL(7, 57, 1), // "y" QT_MOC_LITERAL(8, 59, 6), // "OnBtn1" QT_MOC_LITERAL(9, 66, 14), // "OnSetTextStyle" QT_MOC_LITERAL(10, 81, 17), // "OnSetWindowTitile" QT_MOC_LITERAL(11, 99, 11), // "OnSignalMsg" QT_MOC_LITERAL(12, 111, 12), // "OnBtn2Signal" QT_MOC_LITERAL(13, 124, 14) // "OnCustomSignal" }, "SignalSlot signalMsg1 signalMsg2 " "signalMsg3 signalMsg x y OnBtn1 " "OnSetTextStyle OnSetWindowTitile " "OnSignalMsg OnBtn2Signal OnCustomSignal" }; #undef QT_MOC_LITERAL static const uint qt_meta_data_SignalSlot[] = { // content: 8, // revision 0, // classname 0, 0, // classinfo 10, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 4, // signalCount // signals: name, argc, parameters, tag, flags 1, 0, 64, 2, 0x06 , 3, 0, 65, 2, 0x06 , 4, 0, 66, 2, 0x06 , 5, 2, 67, 2, 0x06 , // slots: name, argc, parameters, tag, flags 8, 0, 72, 2, 0x0a , 9, 0, 73, 2, 0x0a , 10, 0, 74, 2, 0x0a , 11, 0, 75, 2, 0x0a , 12, 0, 76, 2, 0x0a , 13, 2, 77, 2, 0x0a , // signals: parameters QMetaType::Void, QMetaType::Void, QMetaType::Void, QMetaType::Void, QMetaType::Int, QMetaType::Int, 6, 7, // slots: parameters QMetaType::Void, QMetaType::Void, QMetaType::Void, QMetaType::Void, QMetaType::Void, QMetaType::Void, QMetaType::Int, QMetaType::Int, 6, 7, 0 // eod }; void SignalSlot::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) {//调用原方法 auto *_t = static_cast (_o); Q_UNUSED(_t) switch (_id) { case 0: _t->signalMsg1(); break; //信号 case 1: _t->signalMsg2(); break; case 2: _t->signalMsg3(); break; case 3: _t->signalMsg((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; case 4: _t->OnBtn1(); break; //槽 case 5: _t->OnSetTextStyle(); break; case 6: _t->OnSetWindowTitile(); break; case 7: _t->OnSignalMsg(); break; case 8: _t->OnBtn2Signal(); break; case 9: _t->OnCustomSignal((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; default: ; } } else if (_c == QMetaObject::IndexOfMethod) { // 如果是查询函数索引 int *result = reinterpret_cast (_a[0]); // _a[0]存放查询结果 { using _t = void (SignalSlot::*)(); if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SignalSlot::signalMsg1)) { //查询的函数地址是否一致 *result = 0; //保持函数地址 return; } } { using _t = void (SignalSlot::*)(); if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SignalSlot::signalMsg2)) { *result = 1; return; } } { using _t = void (SignalSlot::*)(); if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SignalSlot::signalMsg3)) { *result = 2; return; } } { using _t = void (SignalSlot::*)(int , int ); if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SignalSlot::signalMsg)) { *result = 3; return; } } } } QT_INIT_METAOBJECT const QMetaObject SignalSlot::staticMetaObject = { { &QWidget::staticMetaObject, qt_meta_stringdata_SignalSlot.data, qt_meta_data_SignalSlot, qt_static_metacall, nullptr, nullptr } }; const QMetaObject *SignalSlot::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; } void *SignalSlot::qt_metacast(const char *_clname) { if (!_clname) return nullptr; if (!strcmp(_clname, qt_meta_stringdata_SignalSlot.stringdata0)) return static_cast (this); return QWidget::qt_metacast(_clname); } int SignalSlot::qt_metacall(QMetaObject::Call _c, int _id, void **_a) //元调用 { _id = QWidget::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < 10) qt_static_metacall(this, _c, _id, _a); _id -= 10; } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { if (_id < 10) *reinterpret_cast (_a[0]) = -1; _id -= 10; } return _id; } // SIGNAL 0 void SignalSlot::signalMsg1() { QMetaObject::activate(this, &staticMetaObject, 0, nullptr); } // SIGNAL 1 void SignalSlot::signalMsg2() { QMetaObject::activate(this, &staticMetaObject, 1, nullptr); } // SIGNAL 2 void SignalSlot::signalMsg3() { QMetaObject::activate(this, &staticMetaObject, 2, nullptr); } // SIGNAL 3---信号函数定义 void SignalSlot::signalMsg(int _t1, int _t2) { void *_a[] = { nullptr, const_cast (reinterpret_cast (std::addressof(_t1))), const_cast (reinterpret_cast (std::addressof(_t2))) }; QMetaObject::activate(this, &staticMetaObject, 3, _a); } QT_WARNING_POP QT_END_MOC_NAMESPACE
(1)上述moc文件中#define QT_MOC_LITERAL(idx, ofs, len) 的内容如下:
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, offset)展开后是{ Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, offset } ,
如QT_MOC_LITERAL(0, 0, 10), // "SignalSlot",就是索引0,对应偏移时0,长度SignalSlot是10,其他类推。
(2)qt_meta_data_SignalSlot[]中的 10, 14, // methods,表示信号槽的个数有10个,14是信号槽的标志。 4, // signalCount 信号个数4
(3)上述自定义的信号,可以在moc中找到信号函数定义。并且在信号函数定义中可以看到,当信号触发时,会activate的当前的状态,并将当前最新状态通知对应绑定的staticMetaObject对象。
activate函数在qobjectdefs.h中定义如下:
static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
例如 QMetaObject::activate(this, &staticMetaObject, 3, _a);
当前信号发送者,QMetaObject类型的变量对象,信号索引,参数。通过activate会将信号和槽函数关联连起来。
信号槽的触发过程会调用:qt_static_metacall,qt_metacall,SignalSlot::信号函数()
信号槽机制的文章也可以参考:
读QT5.7源码(四)QMetaMethod 和 QMetaMethodPrivate_春暖花开-CSDN博客
qt元对象系统之 Q_OBJECT宏 - kehuadong - 博客园 (cnblogs.com)
信号槽-原理分析 - 朝十晚八 - 博客园 (cnblogs.com)



