How do you measure your progress ? Part 2

by - 23:06

(continued)

GUI coupling
You will want to subclass Message_ProgressIndicator to couple with your favorite GUI toolkit you are using to develop your application. An alternative choice is to provide simple text output (e.g. for debugging purposes). Either way, you will have to redefine Show() and UserBreak() methods.

In my QOLib (Qt/Open CASCADE Library) I used Qt's QProgressDialog widget to visualize a progress bar and a cancel button. You might want to use your own QWidget consisting of QProgressBar, QLabel and optionally QButton.



Here are some code snippets from QLib:

class QOBase_ProgressIndicator : public Message_ProgressIndicator
{
public:
//! Creates an object.
Standard_EXPORT QOBase_ProgressIndicator (QWidget* theParent,
int theMinVal = 0, int theMaxVal = 100, Qt::WindowFlags theFlags = 0);

//! Deletes the object.
Standard_EXPORT virtual ~QOBase_ProgressIndicator ();

//! Updates presentation of the object.
Standard_EXPORT virtual Standard_Boolean Show (const Standard_Boolean theForce);

//! Returns True if the user has signaled to cancel the process.
Standard_EXPORT virtual Standard_Boolean UserBreak();

protected:

QProgressDialog* myProgress;

public:
DEFINE_STANDARD_RTTI(QOBase_ProgressIndicator)
};

/*! Creates a widget using specified paramters to initialize QProgressIndicator.
\a theMin and \a theMax are also used to set the range for \a this progress
indicator.
*/
QOBase_ProgressIndicator::QOBase_ProgressIndicator (QWidget* theParent,
int theMinVal, int theMaxVal,
Qt::WindowFlags theFlags)
{
QOLib_ASSERT (theMinVal < theMaxVal);
myProgress = new QProgressDialog (theParent, theFlags);
myProgress->setWindowModality (Qt::WindowModal);
myProgress->setMinimum (theMinVal);
myProgress->setMaximum (theMaxVal);
myProgress->setMinimumDuration (500); //the dialog will pop up if operation takes >500ms

SetScale (theMinVal, theMaxVal, 1); //synch up ranges between Qt and Open CASCADE
}

/*! Destroys the associated progress dialog.*/
QOBase_ProgressIndicator::~QOBase_ProgressIndicator ()
{
if (myProgress) {
delete myProgress;
myProgress = 0;
}
}

/*! Updates visual presentation according to currently achieved progress.
The text label is updated according to the name of a current step.

Always returns TRUE to signal that the presentation has been updated.
*/
Standard_Boolean QOBase_ProgressIndicator::Show (const Standard_Boolean theForce)
{
Handle(TCollection_HAsciiString) aName = GetScope(1).GetName(); //current step
if (!aName.IsNull())
myProgress->setLabelText (aName->ToCString());

Standard_Real aPc = GetPosition(); //always within [0,1]
int aVal = myProgress->minimum() + aPc *
(myProgress->maximum() - myProgress->minimum());
myProgress->setValue (aVal);
QApplication::processEvents(); //to let redraw and keep GUI responsive

return Standard_True;
}

/*! Returns True if the user has clicked the Cancel button in QProgressDialog.
*/
Standard_Boolean QOBase_ProgressIndicator::UserBreak()
{
return myProgress->wasCanceled();
}

Open CASCADE ships with a Tcl/Tk-based implementation (see class Draw_ProgressIndicator). Unfortunately, version 6.3.0 has (unintentionally) eliminated the XProgress Draw command that you could use to activate it and use inside the Draw session. I reported this to the folks at OCC, so hopefully they will restore it in upcoming releases. If you are impatient you could simply port it from previous versions.

(To be continued...)

You May Also Like

2 comments

  1. Hello,
    I have setup a similar framework for my projects (really interesting in OCC is the possibility to have sub-ranges) and recently I did some improvements:
    - I wait at least 100ms between an update and another (I use two threads to do that), because Vista Progress bars have an effect that goes too fast/slow depending on the update frequency!
    - Marquee : sometimes you just don't know how long the operation will take (i.e. when you call a BOP). So I use a worker thread with a marquee (you need to update to make it work) while the worker thread finishes.
    Maybe the marquee could be bound to the infinite ranges...
    - Multi-core operations : is your field :) In this case you need multiple "sub ranges" that need to be summed as long as the various tasks make progress. This is something that the base abstract class should start to handle!

    Good bye!
    QbProg :)

    ReplyDelete
  2. Hi QbProg!
    #1. Yep, I had similar caching in previous version of QOLib (visual bar won't get updated until some delta elapses) but then withdrew it relying on a user to not call Show() too frequently (as calculation current time and comparing with delta would add overhead). QProgresBar internally had a mechanism to prevent first popup too fast (I set it to 500ms).
    #2 Not sure if I understood the "marquee" concept. Can you elaborate on it more ?
    Regarding projection of how long the operation will take, yes, it's difficult. I basically set projections the operations subparts (e.g. importing a model is split into 80% on import itself, 5% on updating the data model, and 15% on visualization. Those 80% range (if considered as 100% of its own) is split into 30% of loading and 70% translation and healing. So the bar will get drawn quite smoothly. You just need to experiment with a few representative cases to set those subranges.
    #3 on multi-threading. Current implementation does not support it (I'm going to mention it in Part3). The good news is that OCC is prototyping it now, so chances are it will appear in OCC sooner rather than later.

    Thanks for comments, as usual.

    ReplyDelete