c++qtmutexsignals-slotsqmutex

Wait for buttonPressed() slot to finish before executing buttonReleased()


I have a QPushButton that performs lengthy actions on pressed() and released() signals. How can I make sure that I finished executing all actions of the buttonPressed() slot, before executing the ones of the buttonReleased() slot?

I have tried with QMutex, but the program seems to be stuck in an endless loop when trying to lock upon button release, when the mutex is still locked by the buttonPressed() function:

mymainwindow.h:

#include <QMutex>

// ...

QMutex mutex;

mymainwindow.cpp:

#include <QEventLoop>
#include <QTimer>

// ...

// In the main window constructor:
connect(myButton, SIGNAL(pressed()), this, SLOT(buttonPressed()));
connect(myButton, SIGNAL(released()), this, SLOT(buttonReleased()));

// ...

void MyMainWindow::buttonPressed()
{
    mutex.lock();

    // Here, I do the lengthy stuff, which is simulated by a loop
    // that waits some time.
    QEventLoop loop;
    QTimer::singleShot(1000, &loop, SLOT(quit()));
    loop.exec();

    mutex.unlock();
}

void MyMainWindow::buttonReleased()
{
    mutex.lock();

    // ... (some stuff)

    mutex.unlock();
}

Solution

  • Generally using mutex is a thread sync mechanism, here you do not need thread sync since you are in the same thread. Otherwise I would suggest using QWaitCondition to wait for a flag/mutex to change (i.e. to signal your condition is now ok to go).

    In your case you can just emit a signal once your "buttonPressed" actions are completed (i.e. when your timer ends?). If the end of buttonPressed() function is the when you want to execute your buttonRelease() function then you can just simply use Qt::QueuedConnection's to ensure the correct order of events (I generally don't like direct connections since they act like function calls (or even interrupts - like what I think is happening to you). So the following change might fix this for you in a simple way:

    // In the main window constructor:
    connect(myButton, SIGNAL(pressed()), this, SLOT(buttonPressed()), Qt::QueuedConnection);
    connect(myButton, SIGNAL(released()), this, SLOT(buttonReleased()), Qt::QueuedConnection);
    

    I am not sure if executing your event loop to "simulate" your "long amount of time" will work.... but if you do somthing more like the following to simulate your long execution time:

    QElapsedTimer elapsedTime;
    elapsedTime.start();
    while (elapsedTime.elapsed() < 1000) // millisecs
    {
        // wait....
    }
    

    If this does not work, then just emit a signal at the end of buttonPressed() and set a flag in buttonReleased() such that:

    void MyMainWindow::buttonPressed()
    {
        // actions here
        emit buttonPressedDone();
    }
    
    void MyMainWindow::buttonReleased()
    {
        btnReleased = true;
    }
    
    void MyMainWindow::buttonPressedCompleted()
    {
        if (btnReleased )
        {
            // Do button released actions here
            btnReleased  = false;
        }
        // I am assuming that if the flag is not set then you don't want to do anything... but up to you...
    }
    

    and connect buttonPressedDone --> buttonPressedCompleted

    There are load more options...these are just a few more options for you...