PDA

View Full Version : Custom Progress Dialog while loading QTreeWidget



m3rlin
2nd March 2013, 12:10
HI,

I'd like some advice on best-practice ( if possible with a small code example ).
This is what I am trying to achieve:

In Mainwindow the user clicks the ActionTrigger (a button in the toolbar).
The code in the on_action_triggered stub opens a dialog with .exec()
In the constructor of the called dialog I call a function which takes a long time, thus it appears that nothing is happening (after clicking the button in the toolbar).

I'd like to inform the user with a custom progress dialog about the loading progress of the dialog with the long taking function.

I need to take into account that moving the function (that takes so long) - to a seperate thread is probably "not the way to do it" because it is generating objects (QTreeWidgetItems) on the main thread. The function generates them QTreeWidgetItems based on reading XML (via QDom), processing each toplevel element while comparing them to a QVector<QVector<Int> > which contains +2000 rows.

I have looked into many solutions proposed on forums, books and Qt class references about using QThread, QProcess, QProgressDialog, QFutureWatcher... but I can't seem to get it right that is.

Can anyone shed some brighter light on this topic please?

Santosh Reddy
2nd March 2013, 22:15
The solution you are looking for will be more or less based on the the answer for few questions.
What do you want to inform the user (among the two options below)?
1. Software is working in background (so don't have option other than to wait).
2. Software is working in background and this is how far the software has made progress (the bar shows the progress %).

:)

d_stranz
3rd March 2013, 02:06
Can anyone shed some brighter light on this topic please?

I'd suggest that the way to do this is not to build the tree in the dialog constructor. A common "trick" is to start a single-shot QTimer with 0 ms timeout at the very end of the constructor. Connect the timeout() signal of the timer to a slot in your dialog class. In this slot, build your tree. The net effect of this trick is that the timer's timeout signal won't be handled until after the dialog is displayed (with an empty tree, of course). While you are building your tree, you can use a progress dialog to keep the user informed.



MyDialog::MyDialog( QWidget * parent )
: QDialog( parent )
{
// setupUi(); etc.

QTimer::singleShot( 0, this, buildTree() );
}

void MyDialog::buildTree()
{
int nItems = totalItemsToBeAdded;
QProgressDialog progress( "Building tree", QString(), 0, nItems, this (;
progress.setWindowModality(Qt::WindowModal);

for ( int nItem = 0; nItem < nItems; ++nItem )
{
progress.setValue( nItem );
// add tree widget item
}

// Maybe emit a signal here if needed, or just let the user start interacting with the tree
}


Alternatively, if you have real estate available in your dialog, you can simply put a QProgressBar in it and use that to show the load progress. After the tree is built, you can simply hide() the progress bar.

Another slightly more complex scenario if you don't have the real estate is to use a stack widget in your dialog, one page containing the tree, the other containing the progress bar. When the dialog first comes up, the page containing the progress bar is shown; once the tree is filled, switch to the page containing the tree. This might actually cause filling the tree to be faster since it isn't visible and thus won't be constantly trying to update itself. It will simply update once, when you make it visible by switching pages.

anda_skoa
3rd March 2013, 13:19
You will need to call QApplication::processEvents() after progress.setValue() (not necessarily every iteration but often enought) so the event loop can actually process the progress bar update events.

As for the idea of parallelizing: you can probably run the dom parsing and traversal as well as the checking against your vector in a thread and let it create a list or tree of results that you then simply put into tree view items.

But processing in smaller batches and returning to the event loop in between should already help a lot.

Cheers,
_

m3rlin
3rd March 2013, 20:26
Hi,
I will implement this solution and get back to you. Thank you so much for pointing out how to do this the proper way. I was thinking way too complicated..

Added after 7 minutes:

Hi Santosh,

Thank you for your quick reply. (I forgot to tag myself to receiving email updates, therefore I only now saw you post).
Because the tree is loaded via a for loop I can report progress very precisely, I therefore prefer No. 2.
Thank you for your help.

m3rlin
4th March 2013, 06:13
Hi d_stranz,

I implemented your solution with some tweaks and it works fine now. Thanx again.

Added after 6 minutes:


You will need to call QApplication::processEvents() ... _
I didn't have to. progress.setvalue (as by the example of d_stanz) works fine.


As for the idea of parallelizing..._
I mentioned in my reply that I needed to tweak some... and indeed the parsing I put into a seperate thread.

Thanks for the advice.