将一个对象连接到许多相同类型的对象

Connect one object to many objects of same type

本文关键字:许多相 同类型 对象 对象连接 一个      更新时间:2023-10-16

所以我有以下设置:

|---- Device[1] Controller ---+---- Device[2] |---- Device[3] | : |---- Device[x]

每个对象都在自己的线程中。

对于从device [x]到控制器的通信,这相对容易,我只是将所有设备信号连接到控制器中的插槽,并将设备索引作为参数传递,以便控制器可以识别哪个设备。

然而,我不确定如何最好地在另一个方向(控制器到设备)做同样的事情。我想到的最好的方案是一个类似的方案,我将控制器中的信号连接到每个设备的插槽。我仍然可以传递一个索引,这样如果消息指向Device[1],那么Device[2]和Device[3]可以忽略它。然而,就数据重复而言,这是一个巨大的开销吗?-即数据是否发送三次?

有更好的方法吗?

编辑1 我已经编辑了这个例子,以显示我可以有任何数量的设备。它不是硬编码的数字,所以如果我需要每个设备的信号/插槽,信号将需要动态创建。

编辑2 似乎你可以直接调用方法,像这样:QMetaObject::invokeMethod(devices[x], "handleMessage", Qt::QueuedConnection, Q_ARG(QByteArray msgData)));

这允许我调用任何对象上的槽(排队,所以它是线程安全的)并传递我的数据....但如果我这样做,感觉就像我打破了基本的Qt槽/信号方法。

所以我在网上没有找到很多关于这个问题的信息。我看到人们尝试用以下几种方式来回答这个问题:

1。QSignalMapper

这有它的局限性,我已经在ram的回答中评论过了。

2。直接调用目标槽

您可以直接调用目标槽(可以跨线程边界!),这可以如下所示完成(只是为了完整性而显示):

QMetaObject::invokeMethod(devices[x], "handleMessage", Qt::QueuedConnection, Q_ARG(QByteArray msgData)));

地点:

  • Devices[x]是指向设备对象
  • 的指针。
  • "handleMessage"是槽函数的名称
  • 排队连接用于确保其线程安全,并且不会在调用线程中被调用。
  • Q_ARG(…)是传递给插槽的参数,在本例中是名为msgData的QByteArray。注意:您可以设置多个。

这工作得很好,但它打破了槽/信号的整个方法,因为我们在没有信号或连接的情况下调用外部对象中的槽…有效地在墙上打一个洞,抓住我们想要的东西——所以它也打破了封装原则(更不用说线程边界了)。

3。动态槽位创建

我在qt文档中读到这个,大约一半,但它看起来像可怕的代码,必须编写/维护,所以我甚至没有尝试这个。

我建议的解决方案

所以最后我决定我必须梦想一些东西。因为我只知道在运行时将有多少个设备,所以在每个设备寄存器时动态创建信号很吸引人,但如上所述,不是很好。

但是,我们可以动态实例化对象,其中对象包含一个可用于一对一映射到设备的信号。有效地创建一个"邮筒"对象,其信号可以在运行时连接到等效设备。设置如下所示:

|-------------------------| | | | Postbox[1]--+------Device[1] | Controller Postbox[2]--+------Device[2] | Postbox[3]--+------Device[3] | | : | Postbox[x]--+------Device[x] | | |-------------------------|

其中控制器对象包含一个邮箱对象数组(或向量)。对于每个向控制器注册的设备,它创建一个新的postbox实例,将其信号连接到设备槽,然后将邮箱添加到数组中。然后要发送消息到,例如设备[3],控制器只需调用postboxes[3].postmessage(msgData);这样的函数,postmessage函数发出连接到设备[3]的信号。

据我所知,这是唯一"正确/简单"的方法来做到这一点,因为qt槽/信号似乎没有被设置为这样做消息路由。如果我说错了,请有人纠正我!

使用QSignalMapper

Controller::Controller()
{
    QSignalMapper* signalMapper = new QSignalMapper(this);
    for (int i = 0; i < deviceCount; ++i) {
        Device* d = new Device(...);
        connect(d, SIGNAL(somethingHappened)), signalMapper, SLOT(map()));
        signalMapper->setMapping(d, i);
    }
    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleDevice(int)));
}
void Controller::handleDevice(int id)
{
 .....
}

更多信息:http://doc.qt.io/qt-5/qsignalmapper.html