Qt:在 connect(...) 中使用函子作为发送者
Posted
技术标签:
【中文标题】Qt:在 connect(...) 中使用函子作为发送者【英文标题】:Qt: Use functors as sender in connect(...) 【发布时间】:2020-03-06 15:49:51 【问题描述】:我是 Qt 新手,我想知道如何正确地将仿函数连接到插槽。
这基本上是我尝试过的:
std::function<void ()> sender_function = std::bind(SenderClass::senderFunction, sender);
...
connect(sender, &sender_function, this, &ThisClass::SomeFunction);
但是,它不起作用。有人能指出我正确的方向吗?
感谢和最好的问候, 亚历克斯
编辑: 用例如下。我有一组约 50 个参数,由传感器以固定频率馈送。为了单独绘制数据,我对每个参数都有一个信号,当更新到达时会调用该信号。这些信号我以结构化的方式存储在一个向量中,所以我不需要为每个参数执行查找(更新可以以 100-1000 Hz 的频率提供)。
现在我的想法是,通过在连接上执行一次查找(参数名称-> 信号作为 std::function),绘图可以动态连接到参数信号。
编辑 2: 因为还有进一步的问题,这里有一些代码示例。我希望这能让它更清楚。参数更新的信号如下所示:
signals:
void DataController::port000(double timestamp, Parameter_t p);
Port000 当然是一个例子,我还有 50 个这样的例子,这样每个参数都可以通过我的程序单独发送以进行绘图等。接下来我创建了一个信号向量:
using PortUpdateFunc = std::function<void (double, Parameter_t)>;
QVector<PortUpdateFunc> _port_update_functions;
_port_update_functions[0] = std::bind(&DataController::port000, this, ph::_1, ph::_2);
这使我可以轻松地调用 _port_update_functions[30](stamp, parameter),它会在途中发送我与该端口链接的参数。这是发送方。现在在插槽方面我有这样的东西:
switch(port_id)
case 0: QObject::connect(sender, &DataController::port000, this, &SomeSlotFunction); break;
case 1: QObject::connect(sender, &DataController::port001, this, &SomeSlotFunction); break;
虽然这工作得很好,但我必须明确地将所有端口放在我的开关盒句柄中。如果我有 50 个参数,那就是 50 个端口 000 到端口 049 的案例,这是一种类型的工作。我想做的是这样的:
DataController::PortUpdateFunc port_functor = _port_update_functions[port_id];
QObject::connect(sender, &port_functor, this, &SomeSlotFunction);
所以我可以通过从初始 _port_update_functions 向量中获取具有适当 port_id 的函子来替换 port000、port001、... case-handle。但 Qt 不喜欢这种想法。
【问题讨论】:
connect
中的第二个参数用于在 QObject 内部查找函数指针作为插槽索引。在那里传递函子是没有意义的。
信号必须是信号,即moc生成的成员函数
std::function 后面的函数是一个信号。但 Qt 可能不承认它是这样的。有什么办法吗?
如果您可以使用合适的用例来编辑您的问题,以更清楚地展示您想要实现的目标,那将会很有帮助。
我提供了我的用例作为编辑!希望你能理解,目前在我的手机上,所以没有代码示例。
【参考方案1】:
如果您使用 001、002 变量/方法名称,很可能您做错了什么。另外,最好是 Plot 类不知道具体的端口,不处理信号的选择和连接/断开。
对于您编写的示例,您可以像这样轻松地使用通用信号的签名:
void DataController::portData(int number, double timestamp, Parameter_t p);
或者只是
void DataController::portData(QVariant var);
或使用接口:
struct IParam
...
virtual QVector<QPointF> toVector() const = 0;
struct ConcreteParamName : public IParam
double timestamp;
Parameter_t p;
...
QVector<QPointF> toVector() const override
...
然后emit portData(1, timestamp, p);
或emit portData(QVariant::fromValue(param));
或
emit portData(sharedPointerToParam);
...
void SomeController::portDataSlot(QSharedPointer<IParam> param)
// decide what to draw here:
if(thePortNeedToDraw)
QVector<QPointF> curve = param->toVector();
plot->drawCurve(curve);
// or decide what to receive here:
void SomeController::switchSender(int number, bool enabled)
// without boilerplate switch-case
if(enabled)
QObject::connect(senders[number], &DataController::portData, this, &SomeController::portDataSlot);
else
QObject::disconnect(senders[number], &DataController::portData, 0, 0);
【讨论】:
嗯,我想我明白了!我可以有多个实例但连接到同一个函数,而不是只有一个实例但有多个函数(port000 等)。这听起来很聪明,以至于我现在没有事先考虑过,我觉得真的很愚蠢:D 谢谢!【参考方案2】:这是不可能的。信号参数是字符串或PointerToMemberFunction
。函子只能用作目标。见https://doc.qt.io/qt-5/qobject.html#connect-5。
【讨论】:
std::function 绑定到一个成员函数。我知道没有保证,这就是 Qt 拒绝接受它的原因。但是没有其他解决方案吗? 必须有代码实现“如果调用函子,则调用这些槽”。这就是 moc 为信号生成的,但没有办法为函子生成它。以上是关于Qt:在 connect(...) 中使用函子作为发送者的主要内容,如果未能解决你的问题,请参考以下文章
如何在priority_queue中使用函子作为自定义比较器
进阶学习4:函数式编程FP——函子FunctorMayBe函子Either函子IO函子FolktalePointer函子Monad