一,问题引入:
先引出四个类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);