记录&分享
背景:
使用Qt时,需要对目标控件的某个信号进行相应,但是却不好直接获取到信号发出者的指针。
栗子:
class C :public QObject
{
public:
C(){ emit signalC; }
~C(){ }
signals:
void signalC();
}
class B :public QObject
{
public:
B(){ new C; }
~B(){}
}
class A :public QObject
{
public:
A(){ new B;}
~A(){}
void signalHandler();
}
上述例子中,因为A、C没有直接关系,如果不将A指针向后传都没办法把A、C进行信号关联。
说明:
QMetaObject::invokeMethod:
是一个静态方法,可以根据传递的对象指针及其成员函数名进行方法的调用,该函数的存在让我们的编码更加灵活。有兴趣可以自行去研究。
注:要调用的类型必须是信号、槽,以及Qt元对象系统能识别的类型, 如果不是信号和槽,可以使用qRegisterMetaType()来注册数据类型。此外,使用Q_INVOKABLE来声明函数,也可以正确调用。
QVariant:
这个类非常强大,可以看成是一个万能类型,通过Q_DECLARE_METATYPE声明过的类型基本都能存,借助此类可以实现类似模板函数的功能。
言归正传,附上代码:
// 头文件
#ifndef SIGNALHELPER_H
#define SIGNALHELPER_H
#include <QObject>
#include <QVariant>
class SignalHelper : private QObject
{
Q_OBJECT
public:
static void subscribe(const QString& topic , const QObject* receiver , const char *handleFuncName);
static void notify(const QString& topic , const QVariant& arg = QVariant());
static void remove(const QObject* receiver);
};
#endif // SIGNALHELPER_H
// 源文件
#include "SignalHelper.h"
#include <QDebug>
using Handler = QPair<QObject* , const char*>;
using HandleList = QList<Handler>;
using EventHash = QHash<QString , HandleList>;
static EventHash eventHash;
void SignalHelper::subscribe(const QString &eventName, const QObject *receiver, const char *handleFuncName)
{
if (receiver == nullptr) {
qDebug() << QString("[%1] subscribe failed, receiver is null.").arg(eventName);
return;
}
auto& events = eventHash[eventName];
for (auto& event : events)
if ( event.first == receiver ) {
qDebug() << QString("receiver [%1] has subscribed event [%2].").arg(receiver->objectName()).arg(eventName);
return;
}
events.push_back(Handler(const_cast<QObject *>(receiver) , handleFuncName));
}
void SignalHelper::notify(const QString &eventName, const QVariant &arg)
{
auto iter = eventHash.find(eventName);
if (iter != eventHash.end()) {
for (auto& i : iter.value())
if (!QMetaObject::invokeMethod(i.first , i.second , Qt::AutoConnection , QGenericReturnArgument() , Q_ARG(QVariant,arg)))
qDebug() << QString("[%1] notity failed. receiver: %2, handler: %3").arg(eventName).arg(i.first->objectName()).arg(i.second);
} else {
qDebug() << QString("[%1] notify failed, the event was not subscribed.").arg(eventName);
}
}
void SignalHelper::remove(const QObject* receiver)
{
if ( receiver == nullptr)
return;
for (auto i = eventHash.begin() ; i != eventHash.end() ; ) {
for (auto& handler : i.value())
if (handler.first == receiver) {
i->removeOne(handler);
break;
}
i = i->empty() ? eventHash.erase(i) : ++i;
}
}
// 使用方法:
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
~Widget();
private slots:
void eventHandler(const QVariant& var);
};
#include "Widget.h"
#include "helper/SignalHelper.h"
#include <QDebug>
Widget::Widget(QWidget* parent)
: QWidget(parent)
{
SignalHelper::subscribe("testSignal", this, "eventHandler");
auto btn = new QPushButton("按钮", this);
btn->setCheckable(true);
connect(btn, &QPushButton::clicked, this, [](bool b){
if (b)
SignalHelper::notify("testSignal", "这是测试参数");
else
SignalHelper::notify("testSignal", 123456);
});
}
Widget::~Widget()
{
}
void Widget::eventHandler(const QVariant &var)
{
qDebug() << var.toString();
}
运行效果:
The End!