Search code examples
c++multithreadingqtuser-interfacetoggle

QT: Run a function as long as button is toggled


I want to implement a GUI that receives messages from an external device. The "advancedReceiveExample" is waiting for messages. Once it has received one, it does stuff with it, saves it and terminates.

I want to make my function wait for new messages after receiving one as long as the button is toggled.

I have tried this so far:

void MainWindow::on_pushButton_clicked()
{
    if (ui.pushButton->isChecked()) {
        ui.pushButton->setText("Stop Receiving");
        ui.label_3->setText("Receiving...");

        advancedReceiveExample(ui.comboBox->currentIndex() + 1);
    }
    else
    {
        ui.pushButton->setText("Start Receiving");
        ui.label_3->setText("Not Receiving");
    }
}

This works perfectly fine but as mentioned above it only receives one message. If I do that:

void MainWindow::on_pushButton_clicked()
{
    if (ui.pushButton->isChecked()) {
        ui.pushButton->setText("Stop Receiving");
        ui.label_3->setText("Receiving...");

        while (1)
        {
            advancedReceiveExample(ui.comboBox->currentIndex() + 1);
        }
    }
    else
    {
        ui.pushButton->setText("Start Receiving");
        ui.label_3->setText("Not Receiving");
    }
}

it blocks the function because the state of the button can only be change after the function "on_pushButton_clicked()" has terminated.


Visual Studio 2019 C/C++


EDIT: Okay, I have understood the problem of blocking the thread. Multithreading might be the right option but I am very unexperienced regarding this topic. The <QThread> could be possible. How would you use it?

Do you have suggestions which other library could be used?


Solution

  • Note QT is event-based. If you keep your computer busy inside some function without returning to the main loop frequently, your GUI will freeze.

    What you need to do is slice your action that you want to do into small bits that can repeatedly return to the main loop in order to keep the GUI responsive. (Another method yould be to swap out your action into a separate thread and handle it in parallel, killing the thread when the button is released)

    Probably the simplest method to do what you want is with timers that you arm in the PushButton::clicked slot, and then check in the timer event whether the button is still pressed, and, if yes, do a bit of your action, save state and re-arm the timer to have you return.

    Something along the lines of the following pseudo code should work and execute what you want to do in slices every 10ms:

    MainWindow::onPushButtonClicked () {
       // do the  action, or, alternatively, start a 
       // parallel thread that does it
       do_a_bit_of_action();
       // sets up a timer to call onTimer after 10ms
       QTimer::singleShot (10, this, SLOT(onTimer()));
    }
    
    
    MainWindow::onTimer () {
       // check if button is still held down
       if (pushButton.down) {
          // re-arm timer
          Timer::singleShot (10, this, SLOT(onTimer()));
          // do some more action bits
          do_a_bit_of_action();
       }
       else {
          // kill optional background thread here
       }
       
    }