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

3

Qt是否提供功能以知道特定槽处理的挂起信号数量? 有没有方法清除它们? 例如,如果在连接到给定槽的信号上执行了几个发射操作,某人如何知道这些发射信号的数量?

QMetaObject :: Connection类具有简洁的接口,似乎不提供相关功能。 删除接收信号的对象,从而销毁连接,解决了问题。 但是,是否有一种方法可以在不断开插槽或删除接收对象的情况下完成此操作?


3
请编辑问题,准确说明您的问题出现在什么情况下。很可能这是一个X-Y问题,您不应该询问如何跟踪信号/插槽连接 - Qt并不是为此而设计的,而是要解决导致您认为需要进行此类跟踪的问题。 - Kuba hasn't forgotten Monica
2个回答

3
您提出问题的原因很可能是您的设计有问题。信号和槽机制是一种解耦代码的方法。连接在一起的对象应该自行处理,无论有多少发送者或接收者,肯定不应该尝试跟踪这些问题!
更明智的做法是通过修改设计来修复问题。如果您遇到事件风暴(例如由于在插槽中更改小部件的数据),则插槽应该非常轻量级,并且仅通过调用 update() 来 安排 小部件的更新,而不是强制立即重新绘制。这利用了 Qt 所做的重绘事件压缩。您可能希望 压缩自己的事件

Qt 中的连接类型

在 Qt 中,信号和槽可以使用直接、队列或阻塞连接进行传递。自动类型实际上并不是固定的连接类型。它是一个指令,要求在每次信号发射时将类型解析为直接或队列类型,并针对每个接收者进行解析。
直接连接类似于任何间接函数调用:没有排队,插槽从信号方法的主体内调用:
// all direct-connected slots/functors are invoked before mySignal() returns
emit mySignal();

排队连接会向接收对象线程的事件循环中发布一个 QMetaCallEvent。该事件包含调用的参数,或者携带函数对象。它由 QObject::event() 处理。您可以拦截此类事件。有关详细信息,请参见 this question

0
据我所知,无法访问队列。
首先,如果插槽在GUI线程中的QWidget子类中,则可以直接更新成员变量并调用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的情况下进行过广泛测试。


这是一种使用零持续时间定时器事件压缩的简易事件压缩方法。我不太确定这是否是一种理想的习惯用法。你也可以尝试真正的方法 - Kuba hasn't forgotten Monica
@KubaOber 不确定您所说的“零持续时间计时器事件压缩”具体是什么意思。这种方法适用于任何持续时间的计时器间隔。 - hyde
@KubaOber,此外,与链接问题中的其他方法相比,这种方法是否有特定的缺点,使其成为“穷人的解决方案”? - hyde
你要做的是将小部件的更新速率限制为平台可以处理的速率。由于您不知道任何时刻该速率是多少(由于可用的 CPU 时间等),因此使用固定持续时间计时器是无用的。你真正想要的是在事件队列耗尽一次后更新小部件 - 更新是昂贵的,而不是设置成员。通常的事件压缩已经处理了这个问题,所以只需在setter方法中调用update()来利用事件压缩。否则,如果您正在使用自己的事件,请压缩它们。 - Kuba hasn't forgotten Monica
@KubaOber 这个问题并没有涉及到小部件,但我添加了一个关于 QWidget::update() 特殊情况的注释。我认为这种情况更常见的需要是当信号来自 GUI(用户可以快速点击),或者来自网络或其他外部源时,这些源不需要关心信号目标是否能跟上。那么相对于像你在另一个问题中所示的添加自定义代理类,使用 QTimer 类来压缩单个信号是否存在具体的缺点? - hyde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接