How to Break an Infinite Loop using QPushButton? - qt

I have an infinite loop, inside the loop I want to insert a state whenever I click a button, it will break the current loop.
I've tried several ways like:
if(ui->btnStop->isDown())
{
break;
}
if(ui->btnStop->isChecked())
{
break;
}
and
if(cv::waitKey(10)>=0)
{
break;
}
But, it doesn't work.
I wonder why cv::waitKey doesn't work in Qt, but in a non-Qt project it will work flawlessly.
Are there any other way to break an infinite loop with a QPushButton?
Any help would be appreciated.

It doesn't work because the event processor cannot run whilst execution is locked in your loop. The easiest solution is to simply call QApplication::processEvents() in each loop, this will force the event processor to run.
// Add a boolean to your class, and a slot to set it.
MyClass
{
...
private slots:
void killLoop() { killLoopFlag_ = true; }
private:
bool killLoopFlag_;
}
// In the constructor, connect the button to the slot.
connect( ui->btnStop, SIGNAL( clicked() ),
this, SLOT( killLoop ) );
// Then when performing the loop, force events to be processed and then
// check the flag state.
killLoopFlag_ = false;
while ( true ) {
// ...Do some stuff.
QApplication::processEvents();
if ( killLoopFlag_ ) {
break;
}
}
However you need to ask yourself: Should I be doing long running calculations inside the GUI thread? The answer is usually no.

Related

Start and stop a QThread with a loop

In a QT app, I want to start a loop inside a qthread that reads 2 different sounds (it's a metronome).
I have a function with my process. I want to start it when I click on a button and stop it with another one.
The problem is, when I start it, my app doesn't respond, I can't click on the stop button. I have to stop the app.
#include <QSound>
#include <QEventLoop>
ClickThread::ClickThread(): highClickFile("://high_click.wav"), lowClickFile("://low_click.wav"), isRunning(false)
{
this->highClick = new QSound(highClickFile);
this->lowClick = new QSound(lowClickFile);
this->setPeriod(120);
}
void ClickThread::run()
{ QTimer *timer = new QTimer();
timer ->moveToThread(this);
timer->connect(timer, SIGNAL(timeout()),this, SLOT(process()));
timer ->start();
exec();
}
void ClickThread::process(){
highClick->play();
QThread::msleep(period);
highClick->stop();
lowClick->play();
QThread::msleep(period);
lowClick->stop();
lowClick->play();
QThread::msleep(period);
lowClick->stop();
lowClick->play();
QThread::msleep(period);
lowClick->stop();
}
void ClickThread::setIsRunning(bool set)
{
this->isRunning=set;
}
void ClickThread::setPeriod(unsigned long bpm)
{
this->period = 60000/bpm;
}
Thx for your answers
Stop using QTimer.
The QTimer you have currently is default to a timeout interval of 0. That is going to stuff up the event queue with infinite calls to process(), which will cause serious problems.
You should use this while loop instead:
stopPlaying = false;
while(stopPlaying == false)
{
process();
}
The boolean stopPlaying variable should be declared in your "ClickThread" class definition and used by your stop button to cause the thread to drop out of the loop, terminating the thread.

How can I get access to a QMessageBox by QTest

I am creating some automated GUI tests in my application using QTest.
I can access the widgets from my application using the command:
savePushButton = mainWindow->findChild<QPushButton *>("savePushButton");
It is working fine, but now I have to click on the OK button of a QMessageBox.
I created the QMessageBox in my application like this:
if( something_wrong )
{
QMessageBox::warning(new Widget(), "Title", "Something wrong!");
}
How can I have access to this QMessageBox, and its buttons?
I found a solution on the following link: http://www.qtcentre.org/threads/31239-Testing-modal-dialogs-with-QTestLib .
It uses the command QApplication::topLevelWidgets(); to get a widget list. Then it searches for the message box widget and simulates a key enter (QTest::keyClick(mb, Qt::Key_Enter);) which closes the message box.
Example:
void MyTest::testDialog()
{
QTimer::singleShot(500, this, SLOT(timeOut()));
QVERIFY(functionThatProducesMessageBox());
}
void MyTest::timeOut()
{
QWidgetList allToplevelWidgets = QApplication::topLevelWidgets();
foreach (QWidget *w, allToplevelWidgets) {
if (w->inherits("QMessageBox")) {
QMessageBox *mb = qobject_cast<QMessageBox *>(w);
QTest::keyClick(mb, Qt::Key_Enter);
}
}
}
The header file must contain the Q_OBJECT macro to use the signals and slots mechanism.
Example:
class MyClass: public QWidget
{
Q_OBJECT
public:
...
It worked well for me since the UI (thread) is blocked when the message box appears.
Note: remember to rebuild the project when you add the Q_OBJECT macro.
It often helps to look to Qt's auto tests:
void ExecCloseHelper::timerEvent(QTimerEvent *te)
{
if (te->timerId() != m_timerId)
return;
QWidget *modalWidget = QApplication::activeModalWidget();
if (!m_testCandidate && modalWidget)
m_testCandidate = modalWidget;
if (m_testCandidate && m_testCandidate == modalWidget) {
if (m_key == CloseWindow) {
m_testCandidate->close();
} else {
QKeyEvent *ke = new QKeyEvent(QEvent::KeyPress, m_key, Qt::NoModifier);
QCoreApplication::postEvent(m_testCandidate, ke);
}
m_testCandidate = Q_NULLPTR;
killTimer(m_timerId);
m_timerId = m_key = 0;
}
}
Judging from that code, you can get the message box via QApplication::activeModalWidget(). Testing native (I'm assuming they're native) widgets is difficult, which is likely why they chose to send key events, as you don't need to know e.g. the location of the buttons for those, as you would with a mouse click.

How to draw dash instead of null in QSpinBox?

I need to draw dash instead of null in QSpinBox. Also I need to make dash key pressing equalling null key pressing.
How can I do this?
You can use setSpecialValueText();
QSpinBox spinBox;
spinBox->setSpecialValueText(tr("-"));
You can then check if the special value is selected by connecting valueChanged(QString) function. Note that this is different from valueChanged(int) You can then check the value of the passed string in a slot, and if it is equal to special text, you can do something.
main()
{
connect(spinBox, SIGNAL(valueChanged(QString)), this, SLOT(doSomething(QString)));
}
void doSomething(QString valueStr)
{
if(valueStr == spinBox->specialValueText())
// Do something
else
//Convert valueStr to int and do other stuff
}
Or you could do something like this:
main()
{
connect(spinBox, SIGNAL(valueChanged()), this, SLOT(doSomething()));
}
void doSomething()
{
if(spinBox->value() == 0)
// Do something with dash
else
//Do something with the value
}
For your other question, you need to create a keyPressEvent and check if pressed key is dash or not. If it's dash you can call another function to do something. Edit: BTW, the index of specialValueText() is 0.
Edit: Or you can create a QShortcut in your main function.
new QShortcut(QKeySequence(Qt::Key_Minus), this, SLOT(doSomething()));
Edit continued: doSomething() is a slot function. Put, for example void doSomething(); in the private slots: section of your header file. And in the cpp file define a function similar to this:
void MainWindow::doSomething()
{
ui->spinBox->setValue(0);
//This is the slot called when you press dash.
}
Edit still continued:
You need to declare a protected: function in the header like this:
virtual void keyPressEvent(QKeyEvent *event);
Then you need to define this function in your cpp file. Like this:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Minus)
ui->spinBox->setValue(0);
}
You don't have to connect any signals or slots for this function. It's an event.
That means when dash is pressed ui->spinBox->setValue(0);
Because of that, you need to create a spinBox with a range starting from 0.
spinBox->setRange(0, 100);
That means,
if(spinBox->value() == 0)
//Then specialValueText is selected.

How to use QDBusPendingCallWatcher properly?

I'm trying to use QDBusPendingCallWatcher to watch an async call. Some sample code like this:
{
// interface = new QDBusInterface(...);
QDBusPendingCall pcall = interface->asyncCall("query");
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(handler(QDBusPendingCallWatcher*)));
}
and handler function:
void Client::handler(QDBusPendingCallWatcher* call)
{
QDBusPendingReply<QString> reply = *call;
// do something
}
My questions are:
It looks like QDBusPendingCallWatcher uses shared data pointer inside, is it safe to not manually delete the watcher pointer? Just leave the scope and forget it?
If I can let the smart pointer of pendingcall to do all the tricks, can I use just one QDBusPendingCallWatcher pointer in my class to watch all the async calls? Like this:
{
QDBusPendingCall pcall = interface->asyncCall("query");
watcher = new QDBusPendingCallWatcher(pcall, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(handleOne(QDBusPendingCallWatcher*)));
pcall = interface->asyncCall("anotherQuery");
watcher = new QDBusPendingCallWatcher(pcall, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(handleTwo(QDBusPendingCallWatcher*)));
}
Will this makes a disaster? Or should I use multiple pointers for each call?
Thanks!
Take a closer look at the QDBusPendingCallWatcher documentation:
The slot connected to by the above code could be something similar to the following:
void MyClass::callFinishedSlot(QDBusPendingCallWatcher *call)
{
QDBusPendingReply<QString, QByteArray> reply = *call;
if (reply.isError()) {
showError();
} else {
QString text = reply.argumentAt<0>();
QByteArray data = reply.argumentAt<1>();
showReply(text, data);
}
call->deleteLater();
}
The call of QObject::deleteLater is the key: This means Qt will delete the Object as soon as execution returns to the event loop.
As long as you call deleteLater inside Client::handler(...), you don't need to - more precisely you musn't - call delete watcher; anywhere. The only thing you have to ensure is that noone uses the object behind call after the slot returns.

Qt, QTouchEvents not send to QGraphicsItem

I manually create QTouchEvents and send them to a QGraphicsView's viewport(). While the QGraphicsScene does get the events, my QGraphicsItems (or ...Objects) don't. setAcceptTouchEvents is set to true:
myObject::myObject(QGraphicsItem * parent)
: QGraphicsItem(parent)
{
setAcceptTouchEvents(true);
}
...
bool myObject::sceneEvent( QEvent * event )
{
switch (event->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
//this is never reached?!
return true;
}
return QGraphicsItem::sceneEvent(even);
}
//in a different class:
QTouchEvent * event = new QTouchEvent(t,QTouchEvent::TouchScreen,Qt::NoModifier,states,tpList);
if(m_view->viewport())
{
qApp->postEvent(m_view->viewport(), event);
}
Also, I get "QGestureManager::deliverEvent: could not find the target for gesture" warnings, though I don't even try to grab QGestures anywhere in my program.

Resources