3

考虑以下情况:

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*)表示需要更改本地接收器的状态,如果连接排队,语义是这样的,我不需要更新发生两次。

4

3 回答 3

3

我发现使用 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()));
于 2014-03-15T17:16:17.367 回答
2

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

选项 1:定义类似flush()方法的东西

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

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

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

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

为了实现这一点,在 setter 方法中,您只需将私有布尔变量设置changed为 true,然后将单次计时器(超时为零)启动到类 Foo 中的一个特殊插槽,假设是 slot emitUpdated()。请注意,单次计时器将像调用 setter 方法一样频繁地调用您的插槽,因此插槽必须关心这一点。它只是检查是否changed为真,发出真实信号并将设置更改为假。所以信号只发出一次。

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

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

这会产生最佳性能。确保你总是调用setAthen setB,并且只在setB. 只有发布代码的一个选项(制作一些验证这个“协议”的断言),如果你总是想调用这两种方法。

于 2012-10-09T19:34:42.700 回答
0

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

即当您开始执行do something_heavy 时,断开插槽。执行完成后,重新连接插槽。这样,在执行过程中,不会再处理任何信号。虽然会发出信号,但由于没有连接相应的插槽,因此不会再次执行。

于 2012-10-09T16:20:44.663 回答