I want to use a QMessageBox to announce a short waiting interval to the user.
QMessageBox* box(new QMessageBox(QMessageBox::Information,"Parser","Processing " + mFileName));
box->setStandardButtons(QMessageBox::NoButton);
box->setWindowModality(Qt::WindowModal);
box->show();
QApplication::processEvents();
parser.analyseFile(mFileName);
box->hide();
box->deleteLater();
QApplication::processEvents();
The function only takes few seconds.
The box gets displayed but neither the icon nor the text is shown in time before the function finishes. Why does QApplication::processEvents(); not prevent the program from continuation before the box is completely shown.
Is it possible to achieve the desired behaviour without resorting to threads.
Doing the processing in a separate thread would be preferable, since that would leave the GUI thread free to do things like handle mouse events, window resizes, etc, while the task is being completed; that way the GUI won't "freeze up" temporarily.
If you don't want to spawn a thread, however, you can call processEvents() periodically from within your analyseFile() function and that will give you roughly the same behavior. Try to call it at least every 50mS to avoid sluggish GUI response.
A second possibility might be to add a slot somewhere:
void MyClass :: ParseFile()
{
parser.analyseFile(mFileName);
box->hide();
box->deleteLater();
}
... and then invoke that asynchronously like this:
QTimer::singleShot(0, this, SLOT(ParseFile()));
... that might give the windowing system enough time to finish displaying the QMessageBox before ParseFile() executes, or it might not (in which case you could try to increase the delay argument from 0 to, say, 100 milliseconds instead). That's a little hacky, but it could work.
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
Basically exactly what the title says. I would like to update the text that a button contains every 1 second when the user presses that particular button. I have noted that when the program doesn't have focus it works alright and the text refreshes correctly but when I am hovering over the program or when I am trying to click on it's menu Windows inform me that the program is unresponsive and asks me if I want it terminated. When the loop finishes the program returns to its normal state. Also any action I might have done (like moving it around or closing it) while it was Sleep()-ing is executed after the loop. Here is a bit of code:
case ID_BUTTON_START:
// Code executed when pressing Start Button.
char startButtonText[30]; // Storing next loop text
for (int i=5; i>0; i--)
{
sprintf(startButtonText, "Starting in ... %d", i);
SendMessage(hwndButtonStart, WM_SETTEXT, 0, (LPARAM)(startButtonText));
Sleep(1000);
}
Is this normal? If not what's causing this?
The WndProc does not process messages asynchronously within an application which means all messages are expected to be handled quickly and a return value delivered immediately. You must not Sleep in the UI thread since it will block other UI events from being processed. Any heavy work or synchronous requests/jobs which are likely to take a long time should be performed in worker threads. There are at least three viable options:
Create a new (worker thread) for the task.
If the task is likely to be done often, use a thread pool instead.
Set and subscribe to timer events.
I think the call to Sleep() might be keeping you from returning from the WndProc, so your application is not processing the incomming events for 5 secs. I suggest you try to subscribe to 5 timer events in 1s, 2s,..., 5s. Like when the timer message is recieved the button text must change. I don't know a way how to do that off the top of my head.
I want to execute some code when I press a "Start" button and it have it continue running until I press "Stop".
However, with the following code once I press "Start", I can't press "Stop" anymore:
void MainWindow::on_pushButton_clicked()
{
if (ui->pushButton->text()=="Start") {
ui->pushButton->setText("Stop");
vec spectrum_sensed,Sn,sigma,alpha;
mat ydata;
condition=true;
while (condition) {
cvec uhd_samples = to_cvec(randn(Nsamples),randn(Nsamples));
ydata=spectrum->dft(uhd_samples,Ndft,Nband);
myplot->Plot_data(spectrum_sensed,Ndft);
}
}
else {
ui->pushButton->setText("Start");
condition=false;
myplot->clear_plot();
}
}
Qt's GUI runs in a single thread, and if your code blocks that thread, things like mouse clicks won't ever get handled (because the thread is busy doing nothing but calculating your data), and so your GUI freezes up.
As for how to change your program so that doesn't happen, you have a few options:
You could just throw a call to qApp->processEvents() into your calculation loop. That way Qt will get a chance to handle things like mouse clicks while you are calculating. That's the easiest way to go, but it can bite you if you're not careful -- for example if you've set up some other code so that a mouse click causes your MainWindow to be deleted, then you're likely to crash because after processEvents() returns it find itself executing in the on_pushButton_clicked() method of a now-deleted MainWindow object(!).
You could spawn a separate thread (using the QThread object) and run your calculation loop there. This is probably the most efficient approach, since on a multicore CPU it will let the GUI event loop run on one core while the calculations-loop simultaneously runs on another core, and so neither loop will slow the other one down. Note that this approach only works if your calculation loop doesn't touch any of the Qt GUI stuff -- Qt's GUI widgets are meant to be accessed only by the GUI thread, and if you try to examine them or modify them from a different thread, bad things will happen.
You could "unwrap" your loop so that it's no longer a synchronous loop but rather more like a state machine: That is, move the declarations of your calculation's state variables (spectrum_sensed, Sn, sigma, alpha, and ydata) out of the method, so that they are now member variables of the MainWindow class instead. Then write a Slot method that just does one iteration of your while loop (or maybe a small number of iterations, depending on how long each iteration takes), and then (within a few dozen milliseconds) calls QTimer::singleShot(0, this, SLOT(TheCalculationSlotMethodName()) and returns. That QTimer::singleShot() call will cause your calculation-slot-method to be called again on the next iteration of the Qt event loop, whereupon it can do a little bit more calculation. When the user clicks the "Stop" button, just have your on_stopButton_Pushed() slot set a boolean or something so that your calculation-method will know not to call QTimer::singleShot() anymore. I like this approach because there is little risk of race conditions or re-entrancy problems.
I have a quite lengthy foreach loop in a QDialog. It basically looks like this:
foreach (xxx, xxx) {
... doSomeStuff ...
QApplication::processEvents();
if (m_cancelMapLoading) {
break;
}
}
m_cancelMapLoading is set to true by clicking a "Cancel" button. The QApplication::processEvents(); makes this possible.
This works quite fine, but if the dialog is closed as long as that foreach loop still runs, it continues running. I tried to set m_cancelMapLoading to true in each function closing the dialog, but this does not help.
I also tried to test not only for m_cancelMapLoading being true, but also for isVisible(). This actually stops the dialog, but it re-opens it at once without the GUI elements in it.
Unfortunately, QtConcurrent::run etc. can't be used for the function, because the data structures that are manipulated by the foreach loop are not thread safe.
Is there a convenient way to solve this?
You can use a QTimer and Qt's parent-child structure to your advantage here. QTimer with a timeout value of zero has a special meaning in Qt
As a special case, a QTimer with a timeout of 0 will time out as soon
as all the events in the window system's event queue have been
processed. This can be used to do heavy work while providing a snappy
user interface:
So you could do something like
void Dialog::beginDoingStuff()
{
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(processData());
m_timer->start(0);
}
void Dialog::processData()
{
// Perform one cycle of your process here
}
This will perform the processData() function in the same thread as the rest of the dialog, and when the dialog is destroyed by being closed, the timer will be deleted (because it's parent is the dialog), meaning the processing will stop.
A good and quite easy way to unload your GUI from heavy processing is assigning it to another thread or QtConcurrent.
You could then either poll a "should-I-terminate-yet?" variable or terminate the thread manually when it is no longer needed.
I highly recommend a parallel processing since offers better control rather than doing a "DoEvents"-like queue emptying.
We actually managed to solve the problem by connecting the dialog's finished signal to the click slot of the cancel button. This actually stops the loop in all circumstances.
We also introduced starting the function by a QTimer (for a nicer implementation not blocking the function where it's started), but this does not stop the loop (perhaps because we don't destroy the dialog when it's closed).
Thanks for all help :-)
Basically exactly what the title says. I would like to update the text that a button contains every 1 second when the user presses that particular button. I have noted that when the program doesn't have focus it works alright and the text refreshes correctly but when I am hovering over the program or when I am trying to click on it's menu Windows inform me that the program is unresponsive and asks me if I want it terminated. When the loop finishes the program returns to its normal state. Also any action I might have done (like moving it around or closing it) while it was Sleep()-ing is executed after the loop. Here is a bit of code:
case ID_BUTTON_START:
// Code executed when pressing Start Button.
char startButtonText[30]; // Storing next loop text
for (int i=5; i>0; i--)
{
sprintf(startButtonText, "Starting in ... %d", i);
SendMessage(hwndButtonStart, WM_SETTEXT, 0, (LPARAM)(startButtonText));
Sleep(1000);
}
Is this normal? If not what's causing this?
The WndProc does not process messages asynchronously within an application which means all messages are expected to be handled quickly and a return value delivered immediately. You must not Sleep in the UI thread since it will block other UI events from being processed. Any heavy work or synchronous requests/jobs which are likely to take a long time should be performed in worker threads. There are at least three viable options:
Create a new (worker thread) for the task.
If the task is likely to be done often, use a thread pool instead.
Set and subscribe to timer events.
I think the call to Sleep() might be keeping you from returning from the WndProc, so your application is not processing the incomming events for 5 secs. I suggest you try to subscribe to 5 timer events in 1s, 2s,..., 5s. Like when the timer message is recieved the button text must change. I don't know a way how to do that off the top of my head.