信号槽机制

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博客

qt元对象系统之 Q_OBJECT宏 - kehuadong - 博客园 (cnblogs.com)

信号槽-原理分析 - 朝十晚八 - 博客园 (cnblogs.com)

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QT信号机制是QT框架中一个非常重要的特性,用于对象间的通信。信号机制的基本思想是:一个对象发出一个信号,另一个对象通过连接到这个信号(slot)来处理这个信号。 在QT中,信号都是特殊的函数。信号是一个对象的特殊函数,它定义了一种特殊的行为,当这个对象的某些状态发生改变时,它会自动发出信号也是一个对象的特殊函数,它定义了一种特殊的行为,当某个信号被发出时,与这个信号相连接的会被自动调用。 QT中使用信号机制的步骤如下: 1. 定义信号:在类中声明一个信号函数,使用signals关键字,可以有参数也可以没有参数。 2. 定义:在类中声明一个函数,使用slots关键字,可以有参数也可以没有参数。 3. 连接信号:使用connect函数将信号进行连接。connect函数有多种重载形式,其中最常用的形式是connect(sender, signal, receiver, slot)。 4. 发射信号:使用emit关键字来发射一个信号。 下面是一个简单的例子: ``` class MyObject : public QObject { Q_OBJECT public: MyObject(QObject *parent = nullptr) : QObject(parent) {} signals: void mySignal(int value); public slots: void mySlot(int value) { qDebug() << "Received value: " << value; } }; MyObject obj1, obj2; QObject::connect(&obj1, &MyObject::mySignal, &obj2, &MyObject::mySlot); emit obj1.mySignal(42); ``` 在上面的例子中,我们定义了一个名为MyObject的类,它有一个信号mySignal和一个mySlot。我们使用connect函数将obj1的mySignal信号连接到obj2的mySlot上,然后使用emit关键字来发射一个mySignal信号,并传递一个整数参数。当mySignal信号被发射时,mySlot会被自动调用,输出接收到的整数值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值