I've connected the editingFinished signal of a QLineEdit to a slot in my application showing a QMessageBox if the input is in some way unexpected. Strangely enough the message box is shown twice, so I put a breakpoint where it is executed and had a look at the stack trace. There QMessageBox.exec() calls QApplication::processEvents() which seems to somehow forward and process the same event again.
My stack trace the first time looks sth like this:
MyApp::mySlot()
QLineEdit::editingFinished()
QGuiApplicationPrivate::processMouseEvent()
QEventLoop::processEvents()
QApplication::exec()
and the 2nd time like this:
MyApp::mySlot()
QLineEdit::editingFinished()
QGuiApplicationPrivate::processWindowSystemEvent()
QEventLoop::processEvents()
QDialog::exec()
// stack trace of run #1 here
// [...]
I've already checked for double signal connections or different events being connected to this slot but this doesn't seem to be the problem. Can someone explain what happens here and how to prevent it?
It is a Qt bug that editingFinished is emitted twice, you can read about it here:
https://forum.qt.io/topic/39141/qlineedit-editingfinished-signal-is-emitted-twice
There is also a workaround for it described.
if(lineEdit->text().toDouble()<1000) {
lineEdit->blockSignals(true);
QMessageBox::information(this, "Information", "Incorrect value");
lineEdit->blockSignals(false);
}
Related
I'm beginner learning Qt, and trying to understand a Qt provided example for download operation. In downloadmanager.cpp, a member function is the following:
void DownloadManager::append(const QUrl &url)
{
if (downloadQueue.isEmpty())
QTimer::singleShot(0, this, SLOT(startNextDownload()));
downloadQueue.enqueue(url);
++totalCount;
}
I'm confused to why, if downloadQueue is empty, it will need to activate the startNextDownload() before adding the url. (note that: startNextDownload() ends the program if the downloadQueue is empty)
I'm unsure why: QTimer::signleShot(x, y, z) has been used at all. As I understand it to be, a timer that activates the slot with delay of 0 millisecond.
I could not figure out from looking at Qt Assistant whether singleShot is a one time setup for repeated activation to the slot at given millisecond interval or whether it is one time
Clarification:
I'm a beginner and in examples like:
statement1;
statement2;
I'm used to seeing statement1 running and finishing before moving on to working on statement2. But trying to learn Qt and reading the given example, I see the SLOT(startNextDownload()) being activated after downloadQueue.enqueue(url); has taken place. I am trying to understand why does this work.
This queues a callback in the message queue.
The timer immediately elapses, and a message is posted to the message queue. When the process reaches the main loop for the next time, the startNextDownload() function is called. By this time, the URL is in the queue.
The startNextDownload() function is called from the dispatch context, where it is safe to change window contents. This way, the DownloadManager class can be used from a multithreaded application, where the thread starting the download might be running concurrently with the handler for a Paint event. By invoking it from the same thread that would handle Paint events you can be sure that no such event is being processed, and you can update widgets safely.
If a widget needs to be repainted afterwards, it then asks to be repainted, and the OS will send a Paint event if the widget is currently visible.
Answer to current question title
Every call to QTimer::singleShot(...) is executed on the event loop of the thread where it is invoked **. If invoked from the main thread, it'll be the event loop started with app.exec().
According to the Qt-Network-Manager-Example, this function is called after the network-manager is filled with the URL's so the single-shot will be processed after the queue has been completely filled. Poorly the qt documentation isn't that clear about this topic yet, so for more information about event processing etc please look here.
Answer for old question title
Before I start, the timer is for having the download in an extra thread. So the GUI keeps responsive.
The complete downloadNext() method is recursive. It will be only called once and called till the queue is empty.
See this:
void DownloadManager::append(const QStringList &urlList)
{
foreach (QString url, urlList)
append(QUrl::fromEncoded(url.toLocal8Bit())); //Call for only one URL
...
}
void DownloadManager::append(const QUrl &url)
{
if (downloadQueue.isEmpty())
//I'm only called if the queue is empty! And I will be called after the next line. Not instantly!
QTimer::singleShot(0, this, SLOT(startNextDownload()));
downloadQueue.enqueue(url);
++totalCount;
}
After the queue is empty each method returns and at least the message that the download is done will be printed.
So why does this work?
Please see first chapter below.
you can understand things about Class QTimer before you end up with a solution as you desire, please have a look here for your understanding
I have set a break-point on a slot method and started up gdb to debug my Qt5 application. I would like to know from where in the code this slot gets called from (via the Qt5 signal system).
My naive approach ends in suspension of the program at the break-point, but the stack-trace is all Qt5 internals with no clue as to which part of the program actually sent the signal to this slot (or weather it was a queued or direct invocation:
Is this at all possible? How?
Thanks!
UPDATED
The only time you won't see the signal caller in the stack backtrace is when the connection is queued.
For direct connections you should see something like this:
0 Receiver::baz() <-- slot function that received the signal
1 Receiver::qt_static_metacall()
2 QMetaObject::activate()
3 Sender::bar() <-- function with the name of the signal
4 Sender::foo() <-- function that called emit
5 QWidget::event()
...
For queued connections, the situation is more complicated. But you can add the following to your slot:
QString __sender__ = sender()->metaObject()->className();
This will give you class name of the object, which had sent the signal. You can make it into a macro and sprinkle in your code.
Alternatively, if you have multiple objects of the same class and you need to know which one had sent the signal, you can use the sender() function and compare object address, etc.
Guessing that the signal and slot are connected through a Qt::QueuedConnection, I usually put a breakpoint in every emit of a signal connected to that slot in that case, if there aren't too many of them.
If you otherwise temporarily make it a Qt::DirectConnection (or Qt::BlockingQueuedConnection if across threads) you should be able to see the emission in the stack trace of the emitter thread, waiting for the slot to complete.
I'm building some code where I'm running a while loop and, within the loop, am trying to change the contents of a couple of textboxes with QLineEdit's setText(). However, merely calling setText within the loop does not work; the textboxes only change their actual value once the code has run through, instead of at each iteration.
I have little experience with C++ or Qt, but the project I'm working on must use them. Any help?
EDIT: I'm guessing this must be something simple that I simply am having troubles because of my lack of familiarity/knowledge, but if more information is needed I'll gladly provide it!
The problem is that QT needs control to return to the UI thread's event loop in order to update the QLineEdit's visual appearance. The quick and dirty way to run the event loop is to add QCoreApplication::processEvents() after each call to setText(). The proper way to fix it is to move the blocking process that sets the value of the text box into another thread, expose an updateText(QString text) signal, connect it to the TextBox's setText(const QString & text) slot and emit that signal whenever you want the text to be updated.
Please see my answer to a similar question for more detail: unexplained delay after QProgressBar finish loading
You might also want to check out some of the documentation on QThreads and the Qt signal slot system: http://harmattan-dev.nokia.com/docs/library/html/qt4/threads-qobject.html
In my case, calling only repaint() or processEvents() won't do the job.
Within your function loop, call both QCoreApplication::processEvents(); and repaint();:
for (i;...)
{
//do your calculations
//...
QCoreApplication::processEvents();
repaint();
}
Calling ui->mywidget->update() didn't make any different as well.
(Tested for Qt4.8.3 on Kubuntu 12.10 and Qt5.0.1 on Windows XP)
I am developing Qt application (Qt version 4.7.3) on SBC6000x board.
I have a MessageBox class derived from QDialog. I have made this class singleton.
Whenever a messagebox is to be show I am using .exec method to show it.
There are few places where I need to show messageboxes one after another.
So, to show new messagebox, I have to close previous one and show new one.
e.g. When Messagebox is open and at same time I receive an error from background I have to close the messagebox which is currently shown and show the one with error.
To closes previous dialog I have exposed CloseDlg method from messagebox class and trying to close it.
Inside this CloseDlg I am emitting finished signal.
void CMsgBox::CloseDlg()
{
if (NULL != CMsgBox::m_msgBox)
{
if(CMsgBox::m_msgBox->isVisible())
{
emit CMsgBox::m_msgBox->finished(0);
//QApplication::processEvents();
}
}
}
and calling it as
CMsgBox::CloseDlg();
My show method is :-
int CMsgBox::showMsgBox(Icon icon, const QString &textMsg, const QString &okBtnText)
{
if (CMsgBox::m_msgBox == NULL)
{
CMsgBox::m_msgBox = new CMsgBox();
}
CMsgBox::m_msgBox->setText(textMsg);
CMsgBox::m_msgBox->setIcon(icon);
CMsgBox::m_msgBox->setOkBtnText(okBtnText);
CMsgBox::m_msgBox->exec();
return CMsgBox::m_msgBox->m_btnPressed; //return, unblock the call
}
Again when I call showMsgBox,it is showing me following warning.
QDialog::exec: Recursive call detected
Problem is, it doesn’t return from previous exec call (unless we return, as commented above //).
I tried same with close(), accept(), reject() methods instead of finished() event but nothing worked.
What is the way to return from previous exe call and achieve above scenario? Any help is welcome.
What you have here looks like a race condition. A modal QDialog runs its own event loop, so your application behaves like a multithreaded application and you need to take care of concurrency and race conditions.
When you receive a second in your main event loop, you call CMsgBox::CloseDlg() and CMsgBox::showMsgBox() in quick succession. However, CloseDlg() tells the dialog's event loop to return, but CloseDlg() actually returns before the dialog's event loop is done cleaning up, and showMsgBox() attempts to call exec() on a dialog which hasn't finished exiting yet.
What you need to do is, when you call CMsgBox::CloseDlg(), connect to the finished(int) signal, and only when you receive the finished(int) can you safely exec() the dialog again.
NOTE: When connecting to the finished(int) signal, make sure to use a Qt::QueuedConnection instead of a Qt::DirectConnection which is the default.
So, you need modeless dialog box. As explained in their documentation :
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
Therefore, instead of showing the box with exec(), show it with show().
Alternative to show() method suggested in another answer is, use QDialog::open(). It will return, but will still give you modal dialog, so the rest of the GUI will be disabled until you close it.
I am trying to write a unit test for a GUI application using the QTestLib. The problem is that one of the slots creates a modal dialog using exec() and I found no possibility to interact with the dialog.
The slots which creates the dialog is connected to a QAction. So the first problem is that the test blocks when I trigger the QAction in the test since this results in the call to exec(). Therefore, I tried creating a QThread that performs the interaction. However, this did not help.
Things I already tried (all performed from within the "interaction helper" thread):
Send key clicks using QTest::keyClicks()
Results in error message "QCoreApplication::sendEvent(): Cannot send events to objects owned by a different thread"
Post QKeyEvents using QCoreApplication::postEvent()
Doesn't work, i.e. nothing happens. I guess because the events end up in the event loop of the thread that owns the dialog, which will not be reached until the dialog is closed and exec() returns. See Edit below
Invoking Slots on the dialog using QMetaObject::invokeMethod()
Doesn't work, i.e. nothing happens. I guess for the same reason as postEvent() doesn't work. See Edit below
So the question is: Is there any way to interact programmatically with a modal dialog that was opened using the exec() method?
Edit: Actually, method 3 is working. The problem was a different one:
I passed the arguments to invokeMethod() to the "interaction helper" thread and for some reason, accessing the arguments did not work from that thread (I got no SEG errors but they were simply empty).
I guess that method 2 is also working and I simply had the same problem as with method 3 but I didn't test that.
The solution I use in command line applications which use Qt libraries meant for GUIs is the singleShot, as this answer alludes. In those cases it looks like this:
QCoreApplication app(argc, argv);
// ...
QTimer::singleShot(0, &app, SLOT(quit()));
return app.exec();
So in your case I imagine it would look something like this:
QDialog * p_modalDialog = getThePointer(); // you will have to replace this with
// a real way of getting the pointer
QTimer::singleShot(0, p_modalDialog, SLOT(accept()));
p_modalDialog->exec(); // called somewhere else in your case
// but it will be automatically accepted.
You can keep the interaction in the same thread by delaying its execution until the dialog event loop starts.
For example just before the exec() call, you use either QTimer::singleShot with 0 as interval or QMetaObject::invokeMethod with connection type Qt::QueuedConnection to invoke the slot that needs to be executed while the dialog is shown.
You can also post the events before calling exec().
As soon as the dialog has been constructed after the exec() call, the events will be executed.
For example to test an Esc key press (means reject/close the dialog):
// create a dialog
QDialog d = ...
//post an Escape key press and release event
QApplication::postEvent(&d, new QKeyEvent(QEvent::KeyPress , Qt::Key_Escape, Qt::NoModifier) );
QApplication::postEvent(&d, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Escape, Qt::NoModifier) );
// execute and check result
int ret = d.exec();
QCOMPARE(ret, static_cast<int>(QDialog::Rejected));
related question's answer has some extra details about flushing the event queue during a test:
Qt event loop and unit testing?