Qt 借助QMetaObject::invokeMethod实现信号&槽

记录&分享

背景:

使用Qt时,需要对目标控件的某个信号进行相应,但是却不好直接获取到信号发出者的指针。
栗子:

class Cpublic QObject
{
public:
	C(){ emit signalC; }
	~C(){ }
signals:
	void signalC();
}

class Bpublic QObject
{
public:
	B(){ new C; }
	~B(){}
}

class Apublic 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();
}

运行效果:
R

The End!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值