1、信号槽基本概念
所谓信号槽,实际就是观察者模式_百度百科 (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宏内容如下:
/* qmake ignore 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 <QtWidgets/QWidget>
#include "ui_SignalSlot.h"
#include <QTimer>
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 <qDebug>
#include <qevent.h>
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" <<endl;
emit signalMsg(2,3);
}
void SignalSlot::OnSetTextStyle()
{
ui.lineEdit1->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文件如下
/****************************************************************************
** Meta object code from reading C++ file 'SignalSlot.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.13.0)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include <memory>
#include "../../SignalSlot.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'SignalSlot.h' doesn't include <QObject>."
#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\0signalMsg1\0\0signalMsg2\0"
"signalMsg3\0signalMsg\0x\0y\0OnBtn1\0"
"OnSetTextStyle\0OnSetWindowTitile\0"
"OnSignalMsg\0OnBtn2Signal\0OnCustomSignal"
};
#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 /* Public */,
3, 0, 65, 2, 0x06 /* Public */,
4, 0, 66, 2, 0x06 /* Public */,
5, 2, 67, 2, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
8, 0, 72, 2, 0x0a /* Public */,
9, 0, 73, 2, 0x0a /* Public */,
10, 0, 74, 2, 0x0a /* Public */,
11, 0, 75, 2, 0x0a /* Public */,
12, 0, 76, 2, 0x0a /* Public */,
13, 2, 77, 2, 0x0a /* Public */,
// 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<SignalSlot *>(_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<int *>(_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<void*>(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<int*>(_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<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(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博客