Important: Please read the Qt Code of Conduct -

How to realize waiting?

  • Hey guys!

    I'm quite new to Qt and I need a hint how to solve the following problem:
    Actually I'm writing a program that is needed for the realization of an experiment. It shall be able to control parts of a simulator while taking inputs from the proband. The program itself consists amongst others of a main class which has the QMainWindow and which starts the experiment procedure consisting of sequences. The sequences (class "Sequence") on the other hand are made of blocks (class "Block").
    When I start a sequence (run()), this simply runs a for loop in which the block's run()-function. Each block has a exactly defined duration (default: 30 s) and several objects of the class "Timer" (needed for the experiments). While the block is running there shall different methods be called and the timers shall be updated.

    I tried to do this with a classic sleep-method but that leads to a not responding main window. Therefore I tried to use boost::thread but it seems to lead to problems if there are Qt actions that are in another task.

    Which procedure would you suggest? Is it possible to just use QTimer::singleShot or is threading needed? In which class would you do "the waiting"? In Block or in a subclass "Worker"?

    I'm using C++98 with Qt5 in Visual Studio 2013.

  • Implement each block as a QObject. In the block's run function create a QTimer for each periodic event within the block, and connect each them to a slot that does the work of that event. The run function should not block... It should return immediately. Also give the block a signal to indicate that its run is finished either after a 30 second timer expires or some number of the other events have been processed (or whatever).

    In the main program maintain a list of blocks to run in sequence. Start the first block from the list by calling its run function. Connect its finished signal to a slot that fetches the next block and starts it... And so on.

    There are no blocking waits in this design so the UI will remain responsive.

  • Hey Chris!

    Thanks a lot, this works perfectly for the blocks!
    Each block calls it's follower which was a bit tricky at first, but I just gave each block a pointer to it's follower after initializing.

    The only problem left: How can the last block "tell" it's superior sequence that all blocks run through? As far as I know there is no way in OOP to access a superior class from a object. I thought about giving the blocks a static field "bool finished" which is fetched every x milliseconds by the sequence. I think this could work but I don't consider this as good programming style.
    Also the sequence needs to tell the main process that it's done.

    This is needed because after every sequence there is a pause and the next sequence has to be started manually.

  • It looks like you should have a look to "signal&slot mechanism": of object based on QObject here

    The last block would emit a signal (e.g. finished) and you need to connect to a slot for continuation of tasks.

  • Hello Koahnig,

    yes - I already had a look to this mechanism. This is how I realized calling the end of the block from where I start the next one.
    @ // Stop block if the end is reached
    timeToEndMs = (scheduledEnd - pSsmObject->PTimer->getSecMSec()) * 1000;
    QTimer::singleShot(timeToEndMs, this, SLOT(Stop()));
    I think, it is not possible to go "back" into the sequence that started the block. If I would include the sequence.h to the block.h, I'd have a circle class implementation. But this would be necessary to give the signal a target, wouldn't it?

  • Typically you would have different actions with emitting a signal. You do this with a single shot of a timer. The last object can emit a signal telling it has finished its duties.

    It depends how you have realized your implementations and what is possible.
    Assuming that you are using four different classes A, B, C, and D. Or it may be even the same class, but four objects. You can connect a signal from each class with a slot routine of the next one.
    This can be achieved by "connect":
    You can connect signals of class/object A with slots of class/object B, class/object B with class/object C, class/object C with class/object D, and finally class/object D with class/object A.
    The last connect symbolizes a return call as you require. You are realizing the connect somewhat broken respectively asynchronous. But in principle you could do also completely syncronously.
    Note: if you would do completely syncronously, you will run into a stack overflow with time when you always call the same slot routines. Therefore, it is at least an asyncronous connect required.

  • Okay, I can follow your argumentation till the part with the asynchronous call. I know, that I can change the receiver in singleShot to a pointer to an object in another class. But what I don't understand is, how I get back. Is this what you describe as asynchronous call?

    These are the used classes:

    -> has several Sequences

    Every Sequence
    -> has many Blocks

    So how can I get back from the last block to the next sequence without creating circle class includes when I want to call a slot of Sequence in Block? I don't know how to introduce the class Sequence to the class Block without including, thereby I can't use QTimer::singleShot(value, pSequence, SLOT(doSomething())) because Block doesn't know the class Sequence.
    Sorry, if I'm slow at understanding... Do you have a link to a further documentation?

  • With singleShot you are using a special case of signal&slot mechanism.
    Check the "signal&slot documentation": out.
    There you have different object and their signals are connected to other slots. It looks as a one way street, but actually it is not as described above. So, in principle you can also send a signal from the objects at the end (e.g. Object4) a signal to one object on the left side.

    If you run the signal slot mechanism in one application and thread, with emitting a signal you are basically calling directly the slot. The function/routine emitting the signal is not exited, but the when you return from the slot function, you can basically continue your processing in the function which emitted.

    When your Object4 is calling the same slot of Object1 again you would start another cycle. IN most cases this may work for a couple of rounds or even much more, but ultimately it is nothing else than a endless loop like in a loop which calls itself recursively. Eventually you will hit a ceiling.

    Currently, you seem to use only a fraction of the potential of the signal and slot concept with a single shot of a timer. However, there is much more you can do. Check out the "connect statement": carefully. The are the different "connection types": of your interest in case you like to understand the synchronous and asychronous connections.

Log in to reply