Qt 4.8 - Showing “loading” animation while updating the QGraphicsScene - c++

I’m using Qt 4.8.
I have a big QGraphicsScene to update (it takes 3 secs to append the new QGraphicsObjects).
I would like to show the user that the update is in progress.
In particular I thought about showing a loading wheel on screen and than remove it when the update ends.
The problem here is that I should make the wheel visible and then not visible in the
same thread of the scene update. This because:
It is not allowed to edit graphic properties outside the gui thread.
I cannot move the computation in a “worker thread” since it involves graphics.
This results in the wheel not showing at all, since when the view is updated the wheel
has been already set visible and then not visible again:
showWheel();
/*... big computation involving graphics ...*/
hideWheel();
/*... here GUI is updated, thus the wheel doesn't show up...*/
Is there anything I can do?
Thanks

Presumably you have some event triggering the computation. Instead of using it to trigger the computation, use it to trigger a scheduleComputation()slot. The scheduleComputation slot can call showWheel() and then schedule the doComputation() slot for the end of the next event queue, after which it will return. This will allow the event loop to run and show your wheel, then perform computation. When computation is over, you can call hideWheel() and return to the event loop.
Something like this:
void scheduleComputation()
{
computeScheduler = new QTimer(this);
computeScheduler->setInterval(0);
connect(computeScheduler,SIGNAL(timeout()),this,SLOT(doComputation()));
showWheel();
}
void doComputation()
{
//...Computation Here...
hideWheel();
}
It is also a good idea to keep from blocking the UI thread for long periods of time. This can be done by splitting your long running code into smaller pieces that can be triggered by a timer.

At regular intervals in the "big computation" you can let the event handler run for a little while.

Related

What is the correct way to display widgets without calling QApplication::exec()?

For test purposes I'd like to create and display a widget. For now I only need the widget to render correctly but in the future I may want to extend this so I simulate various events to see how the widget behaves.
From various sources it would appear that the following should work:
QApplication app;
QPushButton button("Hello");
button.show();
// Might also be necessary:
QApplication::processEvents();
But for me the widget does not render correctly. A window is created to display the widget, however it is entirely black.
I can get the widget to render correctly by adding the following lines:
std::this_thread::sleep_for(std::chrono::milliseconds(10));
QApplication::processEvents();
With 10 milliseconds being about the smallest time necessary to get the widget to render correctly.
Does anyone know how to get this to work without the time delay, or know why the delay is necessary?
To test Qt GUI application you need at least QApplication instance and event loop being processed. The fastest way is just use QTEST_MAIN macro, this answer explains in a nice way what it does exactly. However, to have more flexibility (e.g. to use GTest, GMock) you can also simply create QAplication instance in your tests main (no need to call exec).
Then, to have the events processed, you should invoke QTest::qWait. This will process your events for the specified amount of time. It is a good practice to use qWaitFor which accepts a predicate - this way you avoid race conditions in your tests.
In the particular scenario, when you expect some signal to be emitted, you can also use similar functionality of QSignalSpy::wait.
Small example when we want to wait until some parameters are passed from one item to another:
QSignalSpy spy(&widget1, &Widget1::settingsChanged);
widget2->applySettings();
ASSERT_TRUE(spy.wait(5000));
// do some further tests based on the content of passed settings
Why don't you want to have the application run exec ? The process of displaying a widget is not "static". You don't "draw" the widget on the screen, but rather you have an application that listen for various events and receives draw events from the windowing manager. The application can only draw the widget when the windowing manager asks it to.
The reason your second code works is that you wait sufficiently long for the windowing manager to have sent the "draw" request in your conditions. This does not guarantee it will always work.
If you want to guarantee the display of the widget, you need to start a loop and wait until you have received at least one draw event, but even that isn't foolproof.
As expertly described by Vincent Fourmond, widgets are not a one-off deal. The GUI is non-blocking and for this, it needs to run in an event loop.
The exec() method starts this event loop which you mimicked by polling.
While it is possible to combine Qt's event loop with other event loops, I would recommend you a simpler solution:
Proceed your program within the event loop by calling a method when it starts. Find an excellent answer here on how to do this: https://stackoverflow.com/a/8877968/21974
As you mentioned unit testing, there is also a signal you can use for doing checks at the end of the lifecycle (before widgets are destroyed): QApplication::aboutToQuit. This will happen when the last window is closed (programmatically or by the user).

How can I stop a long for loop when the widget (QDialog) running it is closed without multithreading?

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 :-)

QWidget update events but no visual update

Using Qt4.8 on a Mint Linux 12, I implemented a simple window containing a QTableView to show contents of a model. The model data is continually updated (log messages) and the dataChanged() signal is emitted on a regular basis (i.e. every 100ms).
The problem I see is stuttering visual updates on the table.
I installed an event filter on the window that counts updateRequest-type events, which should trigger a widget repaint (also on child widgets, i.e. the tableView). These come in with an average time of ~170ms between them and a standard deviation of ~90ms (which is rather large, I guess). However, the perceived visual update rate is only two or three times a second and I wonder why. It seems that not all updateRequest events trigger a widget repaint or that the window system swallows visual updates.
As a second test, I forced the window to update itself by calling repaint or update every 100ms. Using repaint, I saw a corresponding increase in updateRequest-type events and a decrease of the standard deviation of the gaps; with update, the number did not increase. However, there was only a moderate increase of perceived update rate in both cases.
Also: Is there a good method to measure how often a widget is actually really repainted without having to overload its paintEvent handler? Maybe something from QTest?
Update: I extended my event filter to also catch paintEvent-type events. There are only a one-digit number of those versus > 1000 updateRequest-type events.
You should instrument the event dispatcher's aboutToBlock() and awake() signals, and measure time between them using a QElapsedTimer. The instance of the event dispatcher for the current thread is returned by the static QAbstractEventDispatcher::instance().
If the event loop sleeps for a very small fraction of your time measurement window, it means there's too much stuff going on in the GUI thread. You can keep a count of how long the event loop slept, say, during the last second. If it's below 10%, you can expect slow updates and whatnot. Remember that update events are queued with Qt::LowEventPriority. They will be preempted by standard queued signals and almost all other events.
QApplication::compressEvent discards QEvent::UpdateRequest, if there is already one unprocessed. Thus even repaint(s) can return immediately without painting, if the event is discarded. You can check, if your updateRequest events are discarded by overwriting compressEvent.

how to change GUI while processing

I am new to Qt Programming, but I have basic on C++.
I want to update my GUI while it is processing, example:
while (....)
{
do some calculation...
if (condition fulfill)
change the color of label.
}
However, I realise that I failed to get the result I want (update the GUI while processing). The GUI will only update after the while loop.
Why is it so? Anyone can help?
In addition, I wish to "slower" the color change since the processing is too fast and I can't see the animation. Any idea to do it?
Thank you very much!
Clarification:
Actually I wish to update the GUI while I am processing...Meaning that, if I have 100 iteration, after each iteration I wish to update the GUI immediately.
Use a QTimer. This will allow you to control the speed of your animation and keep your UI responsive.
You have to place your processing code to another thread and update the gui, because like this GUI will be waiting for your process to end and will refresh after its end
read more here:
http://www.qtcentre.org/threads/41545-How-to-refresh-GUI-while-heavy-processing-is-ongoing
http://www.qtcentre.org/threads/32416-Update-GUI-from-another-thread
Forcing the Qt GUI to update
You don't necessarily need a thread.
Calling QApplication::processEvents() will process pending events, including any redraws you may have caused during your processing.
If you wish to animate the color to indicate that the system is currently working, you might want to use QApplication::setOverrideCursor to show a waitCursor, or a QProgressDialog instead.

QT Repaint/Redraw/Update/Do Something

I'm New to QT. I understand that you can force a display refresh, but I've pulled all my hair out trying to figure out how. Here is what I'm specifically trying to do.
I press a button (onClick signal event), which runs code that changes an image (QLabel) on the display, waits for input, and then proceeds by changing a new image (different QLabel). I've tried everything and the display doesn't refresh until the onclick signal event code is complete. Right now, I'm not waiting for user input, I'm using usleep(~500 ms) for testing purposes.
From what I read, QT is event driven meaning that I'm basically creating a bunch of events, that get put in a que, and executed when the (onClick signal event) returns to the (main loop)/(event handler). I don't want to wait until the function is complete, it's going to make programming extremely painful if I have to accomplish this routine entirely based on events.
How can I force the QLabel pixmap to refresh. I've tried everything. Below is all the code I have tried in my onClick signal event handler. (upButton is the name of the QLabel which is a pixmap)
update();
repaint();
ui->upButton->setUpdatesEnabled(TRUE);
update();
repaint();
QPaintEvent paintevent(ui->upButton->childrenRegion());
QPaintEvent * test = &paintevent;
paintEvent(test);
this->changeEvent(test);
ui->upButton->update();
ui->upButton->repaint();
ui->upButton->repaint(ui->upButton->childrenRegion());
repaint();
QApplication::sendPostedEvents();
this->parentWidget()->update();
usleep(100000);
As you can see, I'm just shooting in the dark at this point. I've tried to look at sample code and do all my homework, but I'm lost. Appreciate any help, advice, and or sample code.
I was using sleep to emulate a brief amount of time the computer was waiting for something to happen.
As I stated in my question, I didn't want to use events because it's a whole lot of unnecessary work to accomplish something extremely simply.
Also, the 'event' that needs to take place for the program to continue, is a USB event. Since I'm using an HID class device, there is no way to set an event to happen without a wait loop. USB HID classes don't permit setting interrupts, the OS claims the device.
I managed to get the above to work. I walked through the debugger and noticed the display would refresh before the sleep function. Running the program independently, I got random results with the display refreshing 1% of the time. I got rid of the sleep function, and added some other code in it's place to emulate a delay, and it was fine.
Just for everyone's knowledge, this is possible, it's not forbidden, and it's easy to do with the following:
qApp->processEvents();
qApp is a global external variable in the QApplication header.
Because this USB event is making my flow tricky, I stumbled upon the QWaitCondition Class. I was going to start a process waiting for the USB event. I would wait until the process releases the wait condition for my routine to continue.
But if anyone thinks this is a bad idea, please, speak out. I really do appreciate your feedback PiedPiper and Hostile Fork.
Thank you.
I noticed sometimes when you have multiple layered widgets, or widgets inside of widgets it helps to call their repaint() events.
For example
this->repaint();
this->parentWidget()->repaint();
this->parentWidget()->parentWidget()->repaint();
This is far easier then pushing out any processing to another Thread, or creating additional event handlers.
You shouldn't be waiting for input in your event handler. You need to rethink the logic of your program to use events the way they were intended. All the update() and repaint() calls in your code are unnecessary if you return to the event loop.
If i understood correctly, you have a slot and in this slot, you update the image shown in a QLabel. But you want this change to be displayed before the slot finishes.
If that is the case, issue an update() event, and call qApp->processEvents(). This method processes events that are waiting in the event queue and then returns, therefore this may be what you are after.
PS: an update() may not be necessary at all, i am not sure.