发射信号和插槽 (Qt) 的信息和控制

Information and control on emitted signals and slots (Qt)

本文关键字:信息 控制 信号 插槽 发射 Qt      更新时间:2023-10-16

Qt是否提供功能来了解给定插槽待处理的排队信号的数量?有没有办法清除它们?例如,如果对连接到给定插槽的信号进行了多次发射,那么人们如何知道这些发射信号的数量?

QMetaObject::Connection 类有一个简洁的接口,似乎不提供相关的功能。删除接收信号的对象,从而破坏连接,可以解决问题。但是有没有办法在不断开插槽连接或删除接收对象的情况下做到这一点?

你问这个问题的原因很可能表明你的设计被破坏了。信号和插槽是一种解耦代码的机制。无论有多少发送者或接收者,连接在一起的对象都应该表现自己,当然不应该试图跟踪此类事件!

通过更改设计在源头解决问题会更明智。如果您因事件风暴而烦恼,例如由于更改插槽中小部件的数据,该插槽应该非常轻量级,并且仅通过调用update()安排小部件的更新,但绝不强制立即重新绘制。这利用了Qt完成的重绘事件压缩。您可能也希望压缩自己的事件。

Qt中的连接类型

Qt中的信号和插槽可以使用直接、排队或阻塞连接进行传递。自动类型并不是真正的固定连接类型。它是在每次信号发射时解析每个接收器的类型为直接或排队类型的指令。

直接连接就像任何间接函数调用一样:没有任何东西排队,插槽是从信号方法的主体中调用的:

// all direct-connected slots/functors are invoked before mySignal() returns
emit mySignal();

排队的连接将QMetaCallEvent发布到接收对象线程的事件循环。该事件包含调用的参数,或携带函子。由QObject::event()处理。您当然可以拦截此类事件。有关详细信息,请参阅此问题。

据我所知,无法访问队列。


首先,如果插槽位于 QWidget 子类中,在 GUI 线程中,那么您可以只更新成员变量并调用update(),然后在调用时只使用 paintEvent() 中的当前值。这些是自动压缩的,因此无论调用多少次update()都会只有一个重绘事件。

但是,如果插槽与绘画无关,或者根本不在 GUI 线程中,那么您需要其他东西。


对于许多需要这种情况的情况,一个简单的解决方案是使用第二个插槽和一个延迟为 0(如果需要,甚至更长的延迟)的单次 QTimer

下面是一些示例代码,它应该让你明白我的意思:

// in constructor, set mActualSlotTimer to 
// singleshot, interval 0, parent this (needed for multi-threaded use)
// and connect timeout() to privateActualSlot()

// public slot for receiving the signal from outside
void MyClass::actualSlot(int data) {
    // class member to store the new data value until it can be set
    mNewData = data;
    // restart the timer, no matter if it was already running or not
    mActualSlotTimer.start(); 
}
// "private" slot for actually doing the change
void MyClass::privateActualSlot() {
    // maybe useful: if (this->mData == this->mNewData) return;
    mData = mNewData;
    // do whatever else needs to be done!
}

显然,如果您的公共插槽实际上没有任何参数,那么您不需要 mDatamNewData .

关于这种方法需要注意的一件事是,它适用于所有连接,它不仅限于 Qt::QueuedConnecton .因此,它也使使用Qt::BlockingQueuedConnection变得毫无意义。

免责声明:我简要检查了Qt源代码,似乎使用间隔为0的计时器应该没问题:重新启动计时器将按预期工作。但是,如果似乎仍然有太多的调用要privateActualSlot,那么提供一个合适的间隔可能是必要的。我通常想要一点延迟(例如 5 毫秒)来限制事情,而不是"尽可能频繁",所以没有用间隔 0 对此进行广泛测试。