【问题标题】:Information and control on emitted signals and slots (Qt)发射信号和插槽的信息和控制 (Qt)
【发布时间】:2016-07-12 15:36:28
【问题描述】:

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

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

【问题讨论】:

  • 请编辑问题以准确解释您的问题是在什么情况下出现的。很可能这是一个 X-Y 问题,您不应该询问如何跟踪信号/插槽连接 - Qt 不是为此而设计的,而是如何解决您认为需要进行此类跟踪的任何问题。

标签: c++ qt metaprogramming signals-slots


【解决方案1】:

您提出问题的原因很可能表明您的设计已损坏。信号和槽是一种解耦代码的机制。无论有多少发送者或接收者,连接在一起的对象都应该表现自己,当然不应该尝试跟踪此类事情!

最好通过改变设计从源头上解决问题。如果您正遭受事件风暴,例如由于在插槽中更改了小部件的数据,因此插槽应该非常轻量级,并且仅通过调用update()安排小部件的更新,但从不强制立即重绘。这利用了 Qt 完成的重绘事件压缩。你不妨compress your own events too

Qt 中的连接类型

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

直接连接就像任何间接函数调用:没有任何东西排队,并且从信号方法体内调用槽:

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

排队的连接将QMetaCallEvent 发送到接收对象线程的事件循环。该事件包含调用的参数,或带有函子。由QObject::event() 处理。您当然可以拦截此类事件。详情请见this question

【讨论】:

    【解决方案2】:

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


    首先,如果槽在一个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 进行广泛的测试。

    【讨论】:

    • 这是一个穷人的事件压缩,它使用零持续时间的计时器事件的压缩。这是否是一个可取的成语我不太确定。你也可以do the real thing
    • @KubaOber 不确定“压缩零持续时间计时器事件”到底是什么意思。这种方法适用于任何持续时间的计时器间隔。
    • @KubaOber 此外,与链接问题中的其他方法相比,这种方法是否有一些特定的缺点,这使其成为“穷人解决方案”?
    • 您要做的是将小部件的更新速率限制为平台可以处理的速率。由于您不知道任何时候的先验速率(由于可用的 CPU 时间等),因此使用固定持续时间的计时器是没有用的。您真正想要的是每次耗尽事件队列时更新一次小部件 - 这是昂贵的更新,而不是设置成员。通常的事件压缩已经处理了这个问题,所以只需在 setter 方法中调用update() 来利用事件压缩。否则,如果您使用自己的事件,请压缩它们。
    • @KubaOber 这个问题没有谈论任何关于小部件的内容,但我添加了一个关于QWidget::update() 特殊情况的注释。我认为当信号来自来自 GUI(用户可以快速执行click click click ...)或来自网络时,更经常需要这样的事情,或其他外部源,不需要关心信号的目标是否可以跟上。那么,与添加自定义代理类相比,使用 QTimer 类来压缩像这样的单个信号是否有一些具体的缺点?
    猜你喜欢
    • 1970-01-01
    • 2011-04-08
    • 2012-10-15
    • 2015-07-18
    • 2013-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多