2

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();
}
mimo
  • 2,469
  • 2
  • 28
  • 49
  • Could you explain in more detail, what are you trying to achieve? I find this logic strange. Why do lengthy operations on both when button is pressed, and when button is released? – thuga May 23 '16 at 06:30
  • @thuga The button controls a step motor: on when button pressed, off when I release the click. When I press the button, I send several commands to the electronics that controls the motor. This takes time as I have to wait for responses from the electronics. Also, the motor needs to accelerate slowly to avoid mechanical damage, and this involves an actual QTimer in the sequence of commands to start it. When I release the click, I want to send other commands to stop the motor, but this should be done only after ``buttonPressed()`` has ended. – mimo May 23 '16 at 07:09
  • All your slots are executed in the same thread, so using a mutex is useless. – hank May 23 '16 at 07:10
  • 1
    Add some sort of command queue. When you press the button, add all commands that you use there in the queue. When you release the button, add the stop command to the queue. Don't wait for anything, just respond to the messages you get from the electronics. Every time a command is completed, you get the next one from the queue and send that to your motor. – thuga May 23 '16 at 07:20

2 Answers2

1

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...

code_fodder
  • 15,263
  • 17
  • 90
  • 167
0

Is it necessary connect to release SLOT of your button? You can just connect to destroyed() SIGNAL of your QEventLoop and call buttonReleased() SLOT.

QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
connect(&loop, &QEventLoop::destroyed, this, &MyMainWindow::buttonReleased);
loop.exec();

EDITED BY COMMENT:

QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
connect(&loop, &QEventLoop::destroyed, [=] {
    connect(myButton, &QPushButton::released, this, &MyMainWindow::buttonReleased); 
});
loop.exec();
Shtol Krakov
  • 1,260
  • 9
  • 20
  • Wouldn't that execute ``buttonReleased()`` before button release, if I press it for a long time? – mimo May 23 '16 at 06:32
  • Thanks. I get "error: C3260: ',': skipping unexpected token(s) before lambda body" at the ``connect`` line. Also, I don't understand the syntax of that line. – mimo May 23 '16 at 07:25
  • Sorry, I made a mistake in code. Already edited. About syntax you can read [here](https://wiki.qt.io/New_Signal_Slot_Syntax). – Shtol Krakov May 23 '16 at 07:34