如何将信号作为函数参数传递?

Posted

技术标签:

【中文标题】如何将信号作为函数参数传递?【英文标题】:How to pass a signal as function parameter? 【发布时间】:2017-11-03 18:48:38 【问题描述】:

所以我希望创建我们自己的通用继承复选框类,该类将能够在其构造函数中接收一些值并弹出一个以我们需要的方式完全连接到我们模型的小部件。

目前我们在我们的视图中做这样的事情

connect(checkboxWidget, &QCheckbox::Clicked, this, &VMyView::Signal);

当复选框被点击时,它会从 VMyView 发出信号。

如果我想将该信号作为参数传递给我的新继承类以连接到它自己的连接语句中,我该怎么做?

研究表明我可以传递一个 const char*,但我得到了信号/插槽不匹配的编译错误。

例子

CheckBox(View myView, const char* signal)

    connect(this, &QCheckBox::Clicked, myView, signal);

返回信号和槽参数不兼容的错误。我也试过 SIGNAL(signal) 得到同样的结果。

【问题讨论】:

你能澄清一下吗? @eyllanesc 可以将一个信号连接到另一个信号。 @eyllanesc 你可以转发信号吗?这就是所有这些,就是转发我们的控制器挂钩的信号。我只需要了解如何在函数中将信号作为参数传递。 这不是信号的预期用途,所以请不要这样做。如果创建CheckBox 的人希望在引发CheckBox::Clicked() 时引发信号,那么他们可以根据需要将其连接到自己的信号。 @user1024792 信号是一个简单的指向方法的指针。为什么你认为它应该通过const char * 传递。字符串被用作旧 Qt4 的解决方法。 【参考方案1】:

最终解决方案相当简单

而不是在我的视图中使用它

connect(pCheckbox, &QCheckBox::clicked, this, &MyView::Signal);

我用

    connect(this, &QCheckBox::clicked, View, signal);

信号和通过函数指针进入我的函数

MyCheckBox::MyCheckBox(QWidget* parent, MyView* View, void(MyView::*signal)(bool))

关键是

void(MyView::*signal)(bool) 

也一样

&MyView::Signal

【讨论】:

【参考方案2】:

我认为这里的主要问题是信号不是静态成员函数。因此,它们需要一个指向类实例的指针才能正确调用。所以你不能只传递&VMyView::Signal 之类的东西,因为函数没有相应的this 指针。 (这就是为什么大多数QObject::connect() 重载都需要发送方/接收方对象的实例。)

解决这个问题的一种方法是创建一个函数对象,它包含成员函数指针和指向调用它的对象的指针。这可以传递给QObject::connect() 函数就好了。

这是一个例子:

// objects.h
#include <QtCore>

class Receiver : public QObject

    Q_OBJECT
    public:
        Receiver( QObject *parent = nullptr)
            : QObject(parent)
        
        

        ~Receiver()  

    signals:
        void sig(void);
;


class Sender : public QObject

    Q_OBJECT
    public:
        Sender(std::function<void(void)> &bound_signal, QObject *parent = nullptr) 
            : QObject(parent)
        
            // automatically emit `Sender::sig` on a timer, for testing.
            timer = new QTimer(this);
            timer->setInterval(1000);
            QObject::connect(timer, &QTimer::timeout, this, &Sender::sig);
            QObject::connect(this, &Sender::sig, bound_signal);
            timer->start();
        

        ~Sender()  

    signals:
        void sig(void);

    private:
        QTimer *timer;
;

然后是一个主函数:

// main.cc
#include <QtCore>

#include "objects.h"

int main(int argc, char *argv[])

    QCoreApplication app(argc, argv);
    Receiver receiver; // object to receive the signal

    // Bind the receiver's signal to the instance of the class
    std::function<void(void)> signal = std::bind(&Receiver::sig, &receiver);

    // Create a Sender, which will connect its own signal to the
    // given bound signal
    Sender sender(signal);

    QObject::connect(&receiver, &Receiver::sig,
            []() -> void  qDebug() << "received"; );
    return app.exec();

因此,在您的情况下,Receiver 及其信号将替换为 VMyView 和您想要链接的信号,Sender 将是您实现的自定义复选框类。然后在复选框类的构造函数中,将您想要的任何信号连接到给定的 bound 信号。您还可以传入绑定信号列表,例如std::list&lt;std::function&lt;void(void)&gt;&gt; &amp;bound_signals

不过,我不得不说,我不确定这会给你带来什么。您需要在某处编写连接逻辑,我不明白为什么它需要在复选框类的构造函数中。无论在哪里创建和使用复选框和 VMyView 类,这似乎都是放置连接代码的更好位置。它更明显,不那么复杂,并且有更好的关注点分离。复选框类不必知道或关心它连接到的信号/插槽。应用程序逻辑(即对象在哪里使用)应该定义对象如何相互交互。

【讨论】:

谢谢,我会看看这个@bnaecker。我们的目标是尽可能减少代码的混乱,我们的 UI 是通过编程方式构建的,有些包含 50 多个这样的小部件。它们都只是一个接一个地反复复制/粘贴,连接中的一些东西发生了变化。我希望创建一个为我们完成所有这些工作的类,因此构建表单的函数将是 25 行而不是 250 行。它还有助于防止我的错误,例如在复制/粘贴时忘记更改信号,而追踪这些可能会很痛苦。 关于 std::function 信号 = std::bind(&Receiver::sig, &receiver);。在我的情况下,我希望在接收器本身的实例中执行此操作...但是如果我尝试传入 std::function signal = std::bind(&Receiver::sig, this) ;我在 std::invoke 没有匹配的重载时收到错误... @user1024792 我认为您将接收器/发送器命名法倒退(或者我这样做)。当你构造你的复选框对象时,你想要传递 VMyView 类和你想要连接的信号。 @user1024792 也是。如果您的最终目标是简化连接一堆信号和插槽,请查看QMetaObject::connectSlotsByName(QObject *receiver)。给定名为QObjectQObject 子类senderQMetaObject,它将sender 的所有信号连接到具有特定名称格式的receiver 的信号。有关该格式的详细信息,请查看文档。

以上是关于如何将信号作为函数参数传递?的主要内容,如果未能解决你的问题,请参考以下文章

如何将列表的所有元素作为函数的参数传递而不将列表作为参数传递

如何将函数作为参数传递,带参数? [复制]

linux 信号如何传递两个参数

如何将构造函数(可变参数)作为模板参数传递?

c++ - 如何将void函数作为参数传递?

如何将 QTableWidget 作为函数的参数传递