QT 写的一个信号站,解决跨类多层级的信号和槽连接

一,问题引入:

             先引出四个类A,B,C,D。类B是类A的私有成员变量,类D是类C的私有成员变量,类A和类C是MainWindow类的成员变量。问题:类B需要发信号给类D,类D也需要发信号给类B,如何解决?

二,分析利弊

        按平常的思路,类A定义一个类B的接口指针,同理,得到一个类D的指针,然后在适当的位置connect两个指针?这里应该是在MainWindow类。这样写能实现功能,如果层级够多时,代码可读性很差。

三, 解决方案,建立自定义的信号连接站

1.引入一个单实例类。

        singleton.h

#ifndef SINGLETON_H
#define SINGLETON_H

#define SINGLETON_DEFINE(TypeName)              \
static TypeName* instance()					\
{												\
    static TypeName type_instance;				\
    return &type_instance;						\
}												\
                                                \
TypeName(const TypeName&) = delete;				\
TypeName& operator=(const TypeName&) = delete;



#define SINGLETON_OBJ       instance()
#define SINGLETON_VAR(var)  instance()->var

#endif
2.建立自己的信号站。

        qsignalstation.h

#pragma once
#include <QObject>
#include <QMetaMethod>
#include <singleton.h>

#define CONN_SIGB_SLOTD     "CONN_SIGB_SLOTD"
#define CONN_SIGD_SLOTB     "CONN_SIGD_SLOTB"

using Sender = struct Sender { \
    const QObject *sendObj;
    const char *signal;
    QString specialName;
    Qt::ConnectionType type;
    QString& name() {
        return specialName;
    }
};

using Receiver = struct Receiver { \
    const QObject* recvObj;
    const char *method;
    QString specialName;
    QString& name() {
        return specialName;
    }
};

class QSignalStation : public QObject
{
    Q_OBJECT
public:
    explicit QSignalStation(QObject* parent = 0);
    ~QSignalStation();

    SINGLETON_DEFINE(QSignalStation)
    bool joinSender(const QObject *sender, const char *signal, \
                    const QString specialName, Qt::ConnectionType = Qt::AutoConnection);

    bool joinRecver(const QObject *recvObj, const char *method, \
                    const QString specialName);

private:
    template<class T>
    bool exisitName(std::list<T> list, const QString name, std::function<QString&(T&)> func);

    std::list<Sender> m_senders;
    std::list<Receiver> m_receivers;
    QStringList m_matchs;
};

qsignalstation.cpp

#include "qsignalstation.h"

QSignalStation::QSignalStation(QObject* parent):QObject(parent)
{
    m_matchs.clear();
    m_senders.clear();
    m_receivers.clear();
}

QSignalStation::~QSignalStation()
{

}

template<class T>
bool QSignalStation::exisitName(std::list<T> list, const QString name, std::function<QString&(T&)> func)
{
    auto iterObj = std::find_if(list.begin(), list.end(), [&name, &func](T& data){
        return func(data) == name; });

    if(iterObj == list.end())
        return false;

    return true;
}

bool QSignalStation::joinSender(const QObject *sender, const char *signal, \
                const QString specialName, Qt::ConnectionType type)
{
    if(exisitName<Sender>(m_senders, specialName, &Sender::name)) {
        qCritical("special name is used, please give a new specail name!");
        return false;
    }

    Sender obj = { sender, signal, specialName, type };
    m_senders.push_back(obj);

    auto reciverObj = std::find_if(m_receivers.begin(), m_receivers.end(), \
        [&specialName](Receiver& data){
            return data.specialName == specialName; });

    if(reciverObj == m_receivers.end())
        return false;



    connect(obj.sendObj, obj.signal, \
        reciverObj->recvObj, reciverObj->method, obj.type);
    return true;
}

bool QSignalStation::joinRecver(const QObject *recvObj, const char *method, \
                const QString specialName)
{
    if(exisitName<Receiver>(m_receivers, specialName, &Receiver::name)) {
        qCritical("special name is used, please give a new specail name!");
        return false;
    }

    Receiver obj = { recvObj, method, specialName };
    m_receivers.push_back(obj);

    auto sendObj = std::find_if(m_senders.begin(), m_senders.end(), \
        [&specialName](Sender& data){
        return data.specialName == specialName; });

    if(sendObj == m_senders.end())
        return false;


    connect(sendObj->sendObj, sendObj->signal, \
        obj.recvObj, obj.method, (Qt::ConnectionType)sendObj->type);

    return true;
}
3.引用上述三个文件到工程,然后在自己的类中按下面定义,保证发送类和接受类的标签字段例如:CONN_SIGB_SLOTD,CONN_SIGD_SLOTB 相同即可。
类D:
class D : public QObject
{
    Q_OBJECT
public:
    explicit D(QObject* parent = 0):QObject() {
        QSignalStation::instance()->joinRecver(this, SLOT(onReciverD(const QString)), CONN_SIGB_SLOTD);
        QSignalStation::instance()->joinSender(this, SIGNAL(signalsD(const QString)), CONN_SIGD_SLOTB);
    }

public slots:
    void onReciverD(const QString className) {
        qDebug() << "class D reciver: " << className;
    }

signals:
    void signalsD(const QString& data);
};
类B:
class B : public QObject
{
    Q_OBJECT
public:
    explicit B(QObject* parent = 0):QObject() {
        QSignalStation::instance()->joinRecver(this, SLOT(onReciverB(const QString)), CONN_SIGD_SLOTB);
        QSignalStation::instance()->joinSender(this, SIGNAL(signalsB(const QString)), CONN_SIGB_SLOTD);
    }

signals:
    void signalsB(const QString& data);

public slots:
    void onReciverB(const QString& data) {
        qDebug() << "class B reciver: " << data;
    }
};
4.使用代码很简单,比如类D:

我想发送信号给类B并建立连接:QSignalStation::instance()->joinSender(this, SIGNAL(signalsB(const QString)), CONN_SIGB_SLOTD);

我想接受类B的信号并建立连接: QSignalStation::instance()->joinRecver(this, SLOT(onReciverD(const QString)), CONN_SIGB_SLOTD);

5.喜欢的朋友记得点赞,demo源码地址:

GitHub - soda151314/QSignalStation

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cs821984831

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值