Qt :: QueuedConnection每个事件循环迭代最多调用一次

时间:2012-10-09 16:16:38

标签: qt signals signals-slots slot

考虑以下情况:

class Foo : public QObject {
  Q_OBJECT
  public:
    void set_A(int a) { emit updated(this); } 
    void set_B(int b) { emit updated(this); }
  signals: 
    void updated(Foo*);
}

Foo f;
connect(&f, SIGNAL(updated(Foo*)), something, SLOT(do_something_heavy(Foo*)), Qt::QueuedConnection)

void bar() { 
  f.set_A(5); f.set_B(6);
}

如何确保只有一个信号到达do_something_heavy()来电?

我希望能够使用set_A()并调用do_something_heavy(),但是在调用set_A和set_B的情况下,我不想do_something_heavy()两次。

我是否可以将该特定发送方/接收方对的所有剩余未完成信号排队?最好是emit,而不是接收,但这只是为了简洁和封装 - 我希望updated(Foo*)表示需要更改本地接收者的状态,如果连接排队,语义是这样的,我不需要更新发生两次。

3 个答案:

答案 0 :(得分:2)

我可以为这种情况考虑三种不同的选择。所有选项都不需要将连接排队,因为它们可以保证信号只发出一次。

选项1:定义类似 flush()方法的内容

flush方法然后发出更新的信号,而setter方法则不发出。客户端代码必须手动调用flush()。

选项2:程序空闲时发出信号

如果延迟更新没问题,这只是一个解决方案。这意味着,只有当您的应用程序空闲时,才会发出信号。但是,由于您使用排队连接,情况已经如此。

这是高度动态的,使您甚至可以多次更改两个参数,而且只需调用一次昂贵的更新操作。

为了实现这一点,在setter方法中,你只需将一个私有布尔变量changed设置为true,并启动一个单击定时器(零超时)到类Foo中的一个特殊插槽,比如说插槽{ {1}}。请注意,单击定时器会在调用setter方法时调用您的插槽,因此插槽必须关心这一点。它只检查emitUpdated()是否为真,发出实际信号并将设置更改为false。所以信号只发出一次。

请注意,这已经排队了!单发计时器将被放入事件队列(多次)并调用emitUpdated以发出实际信号一次。因此,您可能希望直接连接到此信号,以避免排队连接带来的双重惩罚。

选项3:假设从每个客户端代码以相同的顺序调用setter

这样可以获得最佳性能。确保始终呼叫changed,然后setA,并仅在setB中发出信号。只有发布代码的选项(使一些断言验证此“协议”)以及是否总是要调用这两种方法。

答案 1 :(得分:2)

我发现使用QTimer组合信号调用很有用。

Foo f;
QTimer timer;
timer.setInterval(0); // this could be set to whatever.
timer.setSingleShot(true);

connect(&f, SIGNAL(updated()),
        &timer, SLOT(start()));
connect(&timer, SIGNAL(timeout()),
        something, SLOT(do_something_heavy()));

答案 2 :(得分:0)

你可以做的是,在do something_heavy()中断开插槽。

即。当你开始执行do something_heavy时,断开插槽。一旦执行完成,重新连接插槽。这样,在执行过程中,不会再处理其他信号。虽然会发出信号,但由于没有相应的插槽连接,它将不会再次执行。