3

Does Qt offer functionality to know the number of queued signals that are pending for a given slot to process? Is there a way to clear them? For example, if several emits are done on a signal connected to a given slot, how could someone know the amount of these emitted signals?

QMetaObject::Connection class has a laconic interface and does not seem to offer related functionality. Deleting the object that receives the signals, hence destroying the connections, solves the problem. But is there a way to do this without disconnecting the slots or deleting the receiving objects?

lackadaisical
  • 1,628
  • 18
  • 21
  • 3
    Please edit the question to explain exactly in what circumstances your problem arises. Most likely it's an X-Y question, and you shouldn't ask about how to track signal/slot connections - Qt wasn't designed for that, but how to fix whatever precipitated your perceived need to do such tracking. – Kuba hasn't forgotten Monica Jul 12 '16 at 18:01

2 Answers2

3

The reason you ask the question most likely indicates that your design is broken. The signals and slots are a mechanism to decouple code. The objects connected together should behave themselves no matter how many senders or receiver are there, and certainly shouldn't attempt to track such matters!

It'd be wiser to fix the issue at the source by altering the design. If you are uffering from an event storm e.g. due to changing a widget's data in a slot, the slot should be very lightweight and only schedule an update of the widget by calling update(), but never forcing an immediate repaint. This leverages repaint event compression done by Qt. You might wish to compress your own events too.

Connection types in Qt

Signals and slots in Qt can be delivered using a direct, queued or blocking connection. The automatic type is not really a fixed connection type. It is an instruction to resolve the type for every receiver, at every signal emission into either a direct or queued type.

The direct connection is like any indirect function call: nothing is queued, and the slot is called from within the body of the signal method:

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

The queued connection posts a QMetaCallEvent to the receiving object thread's event loop. That event contains the arguments of the call, or carries the functor. Is is handled by QObject::event(). You can certainly intercept such events. For details, see this question.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

As far as I know, it is not possible to access the queue.


First of all, if the slot is in a QWidget subclass, in the GUI thread, then you can just update the member variables and call update(), and then just use the current values in paintEvent() when it gets called. These are automatically compressed, so there will be just one repaint event, no matter how many times update() gets called.

However, if the slot is not related to painting, or not in the GUI thread at all, then you need something else.


A simple solution to many cases needing this is to use a 2nd slot and a single-shot QTimer with delay 0 (or even longer delay if desirable).

Here's some example code, which should give you the idea of what I mean:

// 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!
}

Obviously, if your public slot does not actually take any parameters, then you don't need mData and mNewData.

One thing to note about this approach is, it works on all connections, it's not limited to just Qt::QueuedConnecton. As a consequence, it also makes using Qt::BlockingQueuedConnection kinda pointless.

A disclaimery note: I briefly checked Qt source code and it seems using timer with interval 0 should be ok: restarting the timer will work as expected. But if there still seems to be too many calls to privateActualSlot, then providing a suitable interval might be necessary. I have usually wanted a little delay (5 ms for example) to throttle things down a bit more than "as often as possible", so have not tested this extensively with interval 0.

hyde
  • 60,639
  • 21
  • 115
  • 176
  • This is a poor-man's event compression that uses the compression of zero-duration timer events. Whether this is a desirable idiom I'm not quite sure. You could [do the real thing](http://stackoverflow.com/q/20866996/1329652) too. – Kuba hasn't forgotten Monica Jul 12 '16 at 18:03
  • @KubaOber Not sure what exactly you mean by "compression of zero duration timer events". This approach works with any duration timer interval. – hyde Jul 12 '16 at 19:57
  • @KubaOber Also, is there some specific downside to this approach compared to the other approaches in the linked question, which makes it a "poor mans solution"? – hyde Jul 12 '16 at 20:05
  • What you're trying to do is to limit a widget's update rate to the rate the platform can handle. Since you don't know a-priori what rate that is at any moment (due to available CPU time etc.), using a fixed duration timer is useless. What you're really after is to update the widget once per draining of the event queue - it is the update that is expensive, not setting the members. The usual event compression already handles that, so just call `update()` in the setter method to leverage event compression. Otherwise, if you're using your own events, compress those. – Kuba hasn't forgotten Monica Jul 12 '16 at 21:52
  • @KubaOber The question doesn't talk anything about widgets, but I added a note about `QWidget::update()` special case. I think the need for something like this more often happens, when the signal comes *from* the GUI (user can do *click click click ...* fast), or from the network, or other external source, which shouldn't need to care if target of the signal can keep up. So is there some concrete downside to using the QTimer class for compressing a single signal like this, compared to adding a custom proxy class shown for example in your great answers in that other question? – hyde Jul 13 '16 at 07:45