考虑以下情况:
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*)
表示需要更改本地接收者的状态,如果连接排队,语义是这样的,我不需要更新发生两次。
答案 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时,断开插槽。一旦执行完成,重新连接插槽。这样,在执行过程中,不会再处理其他信号。虽然会发出信号,但由于没有相应的插槽连接,它将不会再次执行。