Qt pushbutton slot called twice, although disabled - qt

I have a QPushButton, and a slot connected to its "pressed" signal like that:
connect( &m_oBtnSnapshot, SIGNAL( pressed() ), this,
SLOT( SnapshotClicked() ) );
The Slot is implemented like that:
void
GUI::SnapshotClicked()
{
m_oBtnSnapshot.blockSignals( true );
m_oBtnSnapshot.setDisabled( true );
m_oBtnBenchmark.repaint();
m_oBtnBenchmark.update();
emit( DoSnapshotWork() );
m_oBtnSnapshot.setDisabled( false );
m_oBtnSnapshot.blockSignals( false );
}
So as you can see, i disable the button when i click it, and re enable it when everything is done.
Let's assume the DoSnapshotWork() function takes 5 seconds... While this 5 seconds the button is disabled, but if i click it, the SnapshotClicked() Slot will be called afterwards. Why does disabling a button not prevent me from clicking it?
i alread tried disconnecting the signal on entering the slot and reconnecting it afterwards, but nothing helped.

GUI::SnapshotClicked() is a part of GUI thread, which means, while it runs, your GUI is unaccessible. I assume, signal DoSnapshotWork() is connected with a slot, running in another thread with Qt::QueuedConnection (or Qt::AutoConnection). In that case emitting this signal is asynchronous, which means GUI::SnapshotClicked() is finished long before your slot is done. I guess you should do something like this:
gui.h
public slots:
void onReleaseButton();
gui.cpp
void
GUI::SnapshotClicked()
{
m_oBtnSnapshot.setDisabled( true );
m_oBtnBenchmark.repaint();
m_oBtnBenchmark.update();
emit( DoSnapshotWork() );
}
void
GUI::onReleaseButton()
{
m_oBtnSnapshot.setDisabled( false );
}
Somewhere else:
connect(shapshotWorker, SIGNAL(releaseButton()), gui, SLOT(onReleaseButton()));
...
DoSnapshotWork()
{
...
emit releaseButton();
}
P.S: You need a good reason to use QPushButton::pressed() signal. In most cases you would prefer QPushButton::clicked().

Because the mouse press events are on placed onto the event loop, and wait until your SnapshotClicked() method is complete, by which time the button is enabled again.
A simple solution is to call QCoreApplication::processEvents() immediately after the emit, this will cause the press events to processed whilst the button is still disabled. Or you can have the DoSnapshotWork() method emit a signal when it's finished, and have that enable the button and unblock the signals.
Also,
m_oBtnBenchmark.repaint();
m_oBtnBenchmark.update();
repaint()redraws the widget forcibly, whilst update() calls repaint() via the event loop - do not call them both.

Related

How to Play sound file till it finished in Qt?

I am using QSound to play audio file. Audio file duration is 10 sec.
I have some task after playing the complete 10 sec audio file. So for this I am using QSound::isFinished method, but this always returns false instantly. example
Based on the current wording of the question, I'm assuming you're checking isFinished immediately, which will indeed return false.
I think you're best bet is to use QTimer and connect it's timeout signal to some slot which will check isFinished. You would start the timer when the sound starts playing. When isFinished returns true, you can stop the timer and do whatever it is that needs doing. If it returns false, wait for the next timeout.
// In the .h
// ...
private slots:
void onTimeout();
private:
QTimer* timer;
QSound sound;
// ...
// In the .cpp
// In your constructor
// ...
timer = new Timer();
connect( timer, SIGNAL( timeout() ), this, SIGNAL( onTimeout() ) ),
// ...
// Starting the sound
// ...
sound.play();
timer.start( 10 ); // In milliseconds.
// ...
// onTimeout slot
void MyClass::onTimeout()
{
if( sound.isFinished() )
{
timer.stop();
// Do some things.
}
}
I chose 10 milliseconds in the assumption that the audio file's of different length could be played. QSound is also not guaranteed to start playback immediately.
If you know for a fact that the audio played will always have the same length, you could forgo the isFinished check and change the Timer to a single shot with a duration the same length of the file (plus a bit to account for variable start times). This will result in it only timing out once, and should do so after the file has finished. That's not my preferred method, but it is a possibility.
Use a QSoundEffect instead and connect to the playingChanged signal.
http://doc.qt.io/qt-5/qsoundeffect.html#playing-prop

Strange undocumented QTimer/QEventLoop behaviour after the timer is manually restarted

I have recently stumbled upon this while working with QTimer that calls a function with internal QEventLoop
So, say we have a QTimer instance
QTimer* timer = new QTimer;
somewhere in the constructor we start it and it begins ticking away once every 100ms
timer->start(100);
now the fun part, we connect it to a slot that has internal QEventLoop
void SlotFunction()
{
qDebug() << "entered";
QEventLoop loop;
loop.exec();
}
putting aside how stupid this loop really is, we see that we will never finish processing the slot and timer's subsequent timeouts will keep stacking into execution queue. Everything is ok and as it should be.
What is NOT as it should be comes next: since QEventLoop makes sure our app stays responsive while the slot mindlessly idles away we can make a button and its clicked() slot that looks like:
void OnClicked()
{
timer->start(100);
}
what I am doing here is essentially restarting current timer cycle, nothing less, nothing more. Right? Nope! After this restart, SlotFunction fires again suggesting that tick after timer's restart is not in fact equal to all other ticks that were issued before it...
My only question is : WTF?! Why manually restarting the timer enables it to enter the slot additional time? I've asked on freenode but the only answer I got was "It is as it should be"
I tried this and every click creates another "entered" line.
The main Eventloop cannot handle another event since we are stuck in a new eventloop.
This is quite easy to see when implementing a second slot and also connecting this slot to the timeout signal.
The maineventloop will get stuck when calling the next eventloop and not processing any more queued events.
The timer itself will also not queue any more events, since the queueing up itself would be done in the now stuck main-eventloop. The timer does not run in its own eventloop (thats why Qtimers are no precision timers).
As soon as the button is clicked the new eventloop checks the timer if an event timeout() should be generated.
As soon as the new event is handled we again are stuck in another eventloop...
This will go on until we exit the application.
When exiting the application we see the loops reversing and calling the second slot as often as we clicked the button and ran into the first slot
Code:
#include <QDebug>
#include <QTime>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
timer = new QTimer;
timer->setInterval(2000);
connect(timer,SIGNAL(timeout()),this,SLOT(timerslot()));
connect(timer,SIGNAL(timeout()),this,SLOT(timerslot2()));
timer->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
timer->start(2000);
}
void MainWindow::timerslot()
{
qDebug()<<"In";
QEventLoop loop;
loop.exec();
}
void MainWindow::timerslot2()
{
qDebug()<<"More";
}
Output on start:
In
Output on every click:
In
Output after 3 clicks:
In
In
In
In
Output exiting the application:
In
In
In
In
More
More
More
More

Qt event loop and unit testing?

I'we started experimenting with unit testing in Qt and would like to hear comments on a scenario that involves unit testing signals and slots.
Here is an example:
The code i would like to test is (m_socket is a pointer to QTcpSocket):
void CommunicationProtocol::connectToCamera()
{
m_socket->connectToHost(m_cameraIp,m_port);
}
Since that is an asynchronous call i can't test a returned value. I would however like to test if the response signal that the socket emits on a successful connection (void connected ()) is in fact emitted.
I've written the test below:
void CommunicationProtocolTest::testConnectToCammera()
{
QSignalSpy spy(communicationProtocol->m_socket, SIGNAL(connected()));
communicationProtocol->connectToCamera();
QTest::qWait(250);
QCOMPARE(spy.count(), 1);
}
My motivation was, if the response doesn't happen in 250ms, something is wrong.
However, the signal is never caught, and I can't say for sure if it's even emitted. But I've noticed that I'm not starting the event loop anywhere in the test project. In the development project, the event loop is started in main with QCoreApplication::exec().
To sum it up, when unit testing a class that depends on signals and slots, where should the
QCoreApplication a(argc, argv);
return a.exec();
be run in the test environment?
I realize this is an old thread but as I hit it and as others will, there is no answer and the answer by peter and other comments still miss the point of using QSignalSpy.
To answer you original question about "where the QCoreApplication exec function is needed", basically the answer is, it isn't. QTest and QSignalSpy already has that built in.
What you really need to do in your test case is "run" the existing event loop.
Assuming you are using Qt 5:
http://doc.qt.io/qt-5/qsignalspy.html#wait
So to modify your example to use the wait function:
void CommunicationProtocolTest::testConnectToCammera()
{
QSignalSpy spy(communicationProtocol->m_socket, SIGNAL(connected()));
communicationProtocol->connectToCamera();
// wait returns true if 1 or more signals was emitted
QCOMPARE(spy.wait(250), true);
// You can be pedantic here and double check if you want
QCOMPARE(spy.count(), 1);
}
That should give you the desired behaviour without having to create another event loop.
Good question. Main issues I've hit are (1) needing to let app do app.exec() yet still close-at-end to not block automated builds and (2) needing to ensure pending events get processed before relying on the result of signal/slot calls.
For (1), you could try commenting out the app.exec() in main(). BUT then if someone has FooWidget.exec() in their class that you're testing, it's going to block/hang. Something like this is handy to force qApp to exit:
int main(int argc, char *argv[]) {
QApplication a( argc, argv );
//prevent hanging if QMenu.exec() got called
smersh().KillAppAfterTimeout(300);
::testing::InitGoogleTest(&argc, argv);
int iReturn = RUN_ALL_TESTS();
qDebug()<<"rcode:"<<iReturn;
smersh().KillAppAfterTimeout(1);
return a.exec();
}
struct smersh {
bool KillAppAfterTimeout(int secs=10) const;
};
bool smersh::KillAppAfterTimeout(int secs) const {
QScopedPointer<QTimer> timer(new QTimer);
timer->setSingleShot(true);
bool ok = timer->connect(timer.data(),SIGNAL(timeout()),qApp,SLOT(quit()),Qt::QueuedConnection);
timer->start(secs * 1000); // N seconds timeout
timer.take()->setParent(qApp);
return ok;
}
For (2), basically you have to coerce QApplication into finishing up the queued events if you're trying to verify things like QEvents from Mouse + Keyboard have expected outcome. This FlushEvents<>() method is helpful:
template <class T=void> struct FlushEvents {
FlushEvents() {
int n = 0;
while(++n<20 && qApp->hasPendingEvents() ) {
QApplication::sendPostedEvents();
QApplication::processEvents(QEventLoop::AllEvents);
YourThread::microsec_wait(100);
}
YourThread::microsec_wait(1*1000);
} };
Usage example below.
"dialog" is instance of MyDialog.
"baz" is instance of Baz.
"dialog" has a member of type Bar.
When a Bar selects a Baz, it emits a signal;
"dialog" is connected to the signal and we need to
make sure the associated slot has gotten the message.
void Bar::select(Baz* baz) {
if( baz->isValid() ) {
m_selected << baz;
emit SelectedBaz();//<- dialog has slot for this
} }
TEST(Dialog,BarBaz) { /*<code>*/
dialog->setGeometry(1,320,400,300);
dialog->repaint();
FlushEvents<>(); // see it on screen (for debugging)
//set state of dialog that has a stacked widget
dialog->setCurrentPage(i);
qDebug()<<"on page: "
<<i; // (we don't see it yet)
FlushEvents<>(); // Now dialog is drawn on page i
dialog->GetBar()->select(baz);
FlushEvents<>(); // *** without this, the next test
// can fail sporadically.
EXPECT_TRUE( dialog->getSelected_Baz_instances()
.contains(baz) );
/*<code>*/
}
I had a similar issue with Qt::QueuedConnection (event is queued automatically if the sender and the receiver belongs to different threads). Without a proper event loop in that situation, the internal state of objects depending on event processing will not be updated. To start an event loop when running QTest, change the macro QTEST_APPLESS_MAIN at the bottom of the file to QTEST_MAIN. Then, calling qApp->processEvents() will actually process events, or you can start another event loop with QEventLoop.
QSignalSpy spy(&foo, SIGNAL(ready()));
connect(&foo, SIGNAL(ready()), &bar, SLOT(work()), Qt::QueuedConnection);
foo.emitReady();
QCOMPARE(spy.count(), 1); // QSignalSpy uses Qt::DirectConnection
QCOMPARE(bar.received, false); // bar did not receive the signal, but that is normal: there is no active event loop
qApp->processEvents(); // Manually trigger event processing ...
QCOMPARE(bar.received, true); // bar receives the signal only if QTEST_MAIN() is used

Qt: How to update the GUI when the model changes?

I use checkboxes in my GUI to toggle bits in a class controlling a maschine connected by signals/slots.
I also need the opposite direction, because if some commands to the maschine toggle the same bits, my GUI should reflect the changes.
My problem is:
When I click the checkbox, its state is not yet updated when the signal is sent.
So the first signal arrives at the maschine, the bit gets toggled, the maschine responds with the second signal and arrives at the GUI handler method 'updateCheckBoxXYZ'.
Now I want to figure out if I need to update the checkbox.
If the whole chain is started by user, the checkbox will be updated automatically at last.
If an internal command directly toggled the bit, the checkbox will need to be checked to reflect the internal change.
Because the checkbox has not been checked yet, I can not ask for 'isChecked()'.
What should I do to not getting trapped in an endless loop?
Update:
I just tried something like the following code:
// slot called by toggled(bool)
void DialogXY::checkBoxXYChanged(bool bState)
{
if (bState != m_bGuiStateXY)
{
m_bGuiStateXY = bState;
emit GuiXYChanged(bState);
// optional: .. do some GUI related things ..
}
}
// slot called on signal 'GuiXYChanged(bState)'
void Machine::changeXY(int iModul, bool bState)
{
if (bState != m_bMachineStateXY)
{
emit MachineXYChanged(bState);
}
// .. change machine configuration ..
}
// slot called on signal 'MachineXYChanged(bState)'
void DialogXY::updateCheckBoxXY(bool bState)
{
if (bState != m_bStateXY)
{
ui.checkBoxXY->setChecked(bState);
// will signal toggled()
}
}
But I need extra variables for each GUI item,
I have to initialize then correctly, etc.
I would prefer something more elegant.

Qt force QCheckBox to emit signal on setChecked

If I called QCheckBox::setChecked( x ) the toggled signal is only emitted if x is not the same as the current checkbox state. I understand the logic behind this, to avoid signaling if nothing has changed. However, in some situations where I have a more complicated widgets setup, I need the signal to always be emitted. This ensures anybody who has connected to the checkbox will receive the first state.
Is there a way to have QCheckBox::setChecked(bool) emit a signal regardless of whether the state has changed?
My simple workaround now is to just force the checkbox into multiple states by doing setChecked(!x) and setChecked(x). I was hoping for a more correct way of doing this.
Looking into the QAbstractButton implementation, I found the following lines of code:
if (!d->checkable || d->checked == checked) {
if (!d->blockRefresh)
checkStateSet();
return;
}
where checkStateSet is a virtual function. QCheckBox overrides this and emits a stateChanged() signal only if the state changed.
I haven't tested this, but I think d->blockRefresh is set to false if you call QCheckBox::setChecked( ... ) directly.
If this is the case, it means you could subclass QCheckBox and override the checkStateSet() method to something like this:
void MyCheckBox::checkStateSet()
{
QCheckBox::checkStateSet();
if( m_oldState == checkState() )
{
// emit the signal here, as QCheckBox::checkStateSet() didn't do it.
emit stateChanged( m_oldState );
}
else
{
// don't emit the signal as it has been emitted already,
// but save the old state
m_oldState = checkState();
}
}
where the header file contains
private:
Qt::CheckState m_oldState;
which must be initialised to Qt::Unchecked in the constructor.
Here is another solution which may or may not be possible for your case:
If you can be 100% sure that your signals and slots are connected before the checkbox has a chance to change its state, every connected class can initialize itself safely assuming the checkbox is not checked. This is because checkboxes are always unchecked upon construction.
This way you might not have to call setChecked() after connecting the signals.
However, This approach does not work if there is a chance a signal gets connected after the checkbox has already changed. I'm not 100% fond of this approach myself but it might be an option for you nevertheless.
One way would be to subclass QCheckBox and implement the emitting of signals in that where you need it, for example :
class MyCheckBox : public QCheckBox
{
Q_OBJECT
public:
MyCheckBox(QWidget *parent = 0) : QCheckBox(parent) {};
virtual void setChecked(bool checked) {
QCheckBox::setChecked(checked); emit checkWasSet(checked);
};
signals:
void checkWasSet(bool value);
};
Now use this class instead of the regular QCheckBox class, and you can connect to the checkWasSet() signal for whenever the check state is set.
You could emit the signal with the current state yourself:
checkbox.stateChanged.emit(checkbox.checkState())

Resources