Domanda

In Qt you can connect two objects by setting up a signal in one object a slot in the other and then connecting them using "connect()".

Now by emitting a signal in one object it is sent to the second object. I have a system that takes user inputs and if there are too many user inputs I want my "queue" to fill up and not accept any more inputs.

I could implement a reply mechanism on the receiving object, but I want to know if we can make a queue size of (for example) 1. So only one message will be handled, and any new emittions are simply chucked away until the "pipe" is has space.

Is this possible in Qt?

In my case the two objects are in different threads and have a queued connection (if that makes any difference)...

MainWindow::MainWindow()
{
    // Make object 1, stick it in another thread
    MyObjType1 *obj1 = new MyObjType1();
    anotherThread = new QThread; // anotherThread  is type QThread *
    obj1->moveToThread(anotherThread);
    anotherThread->start();

    // Make object 2, connect a signal to obj1
    MyObjType2 *obj2 = new MyObjType2();
    connect(obj2, SIGNAL(obj2Signal(int), obj1, SLOT(obj1Slot(int), Qt::QueuedConnection);

    // Hammer obj1 with signals to its queue
    for (int i = 0; i < 100000; i++)
    {
        emit obj2->obj2Signal(i);
    }
}

So the idea would be that obj1 gets lots of signals, it handles the first one, and somehow throws the others away until it finishes, then takes on the next one that is emitted.

È stato utile?

Soluzione

With a queued connection, for each slot connected to a signal there is a QMetaCallEvent posted to the connected slot object's event queue. The events are delivered when the event loop runs. The code below outputs:

about to emit
done emitting
in aSlot() 
class MyObject {
  Q_OBJECT
  Q_SIGNAL void aSignal();
  Q_SLOT void aSlot() { qDebug() << "in aSlot()"; }
public:
  MyObject(Qt::ConnectionType conn = Qt::AutoConnection) {
    // QObject::connect() defaults the connection type to Qt::AutoConnection,
    // we merely duplicate this behavior.
    connect(this, SIGNAL(aSignal()), SLOT(aSlot()), conn);
    qDebug() << "about to emit";
    emit aSignal();
    qDebug() << "done emitting";
  }
};
int main(int argc, char ** argv) {
  QCoreApplication app(argc, argv);
  MyObject obj(Qt::QueuedConnection);
  QCoreApplication::processEvents();
  return 0;
}

The problem can now be reformulated to: How to force removal of duplicate QMetaCallEvent events from the event queue? This is known as event compression. I have already provided a canonical answer to that question. For user input, you want the most recently emitted signal to be retained, not the oldest one, but I've implemented both behaviors in the answer code.

Using the code from my answer, your example merely needs the following in the main() function:

int main(int argc, char ** argv) {
    CompressorApplication<QApplication> app(argc, argv);
    app.addCompressedSignal(MyObjType2::staticMetaObject.method(MyObjType2::staticMetaObject.indexOfSignal("obj2Signal(int)")));
    MainWindow w;
    w.show();
    return app.exec();
}

Note: If one were connecting objects with the default Qt::AutoConnection and the objects were in the same thread, then the concept of a queue wouldn't apply at all. The slot is called before the signal function returns and nothing needs to be queued! The code below will output:

about to emit
in aSlot()
done emitting
// MyObject as above
int main(int argc, char ** argv) {
  QCoreApplication app(argc, argv);
  MyObject obj;
  return 0;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top