How do you measure your progress ? Part 1

It's important to set measurable goals in your life and to periodically check how you advance. Your application should also communicate its status in execution of a long operation, otherwise your users may decide the application hung up and to kill it.

Open CASCADE offers a mean to help you to signal that your app is still alive. It's Message_ProgressIndicator, a class manipulated by handle that you can pass to some algorithms. Currently it is supported by IGES and STEP translators, as well as by BRep loader (starting from version 6.3). Well, not much but still is a good start. OCC team might want to extend its use into some CPU-hungry algorithms (e.g. Boolean operations or others).

Nonetheless, you might want to use it even in your own algorithms thanks to its rich capabilities.
Let's consider them more closely.

Message_ProgressIndicator extends a simple concept of a progress indicator as a container of integer [min, max] range and a current value.

First nice thing is that Message_ProgressIndicator offers a range of double (not int) range and a custom step.

Handle(MyProgressIndicator) anIndicator = new MyProgressIndicator;
Standard_Real aStartAngle = 0.5 * PI;
Standard_Real anEndAngle = 1.5 * PI;
Standard_Real aDelta = PI / 6.;
anIndicator->SetRange (aStartAngle, anEndAngle);
anIndicator->SetStep (aDelta);

for (Standard_Real aCurrentAngle = aStartAngle; aCurrentAngle <= aStartAngle; aCurrentAngle += aDelta, anIndicator->Increment()) {

To get a current value on the global range use Message_ProgressIndicator::GetPosition() which returns a value in the range [0, 1].

Next cool thing is that the indicator supports nested regions. That is extremely valuable if your operation is a subpart of a larger operation it may have no idea of. Imagine that your algorithm of building custom visual presentation (AIS_InteractiveObject subclass) can be an ending step of both your complex modeling algorithm and of simple restoring a model from a file.

An operation can allocate a sub-range for its sub-operations so that they report progress within their sub-ranges. The progress indicator will take care to map local value into a global range.

void MyLongOperation ()
Handle(Message_ProgressIndicator) anIndicator = new MyIndicator;
anIndicator->SetRange (0, 100); //100% complete

anIndicator->NewScope (60., "The longest suboperation"); //first sub operation takes 60%
MyLongSubOperation1 (anIndicator);
anIndicator->EndScope ();

anIndicator->NewScope (30.); // 30%
MyLongSubOperation2 (anIndicator);
anIndicator->EndScope ();

anIndicator->NewScope (10.); // 10%
MyLongSubOperation3 (anIndicator);
anIndicator->EndScope ();


void MyLongSubOperation1 (const Handle(Message_ProgressIndicator)& theIndicator)
Standard_Integer aNbSteps = ...; //small chunks of work
theIndicator->SetRange (0, aNbSteps);
for (int i = 1; i <= aNbSteps; i++, theIndicator->Increment()) {


In the above code the entire global range has been split into 3 non-equal chunks proportionate to estimated duration of each MyLongSubOperation...(), and each is executed in its own subrange. Thus SetRange() in MyLongSubOperation1() affects its allocated subrange.

Third, sub-ranges can have their own names what can be used by displaying widgets to reflect a current phase. The name can be specified with SetName() or directly in NewScope().

Indicator can also be used to signal a user's action to break the operation. Redefine and use UserBreak() virtual method.

There are other nice features like support of infinite regions or Message_ProgressSentry class, which is a convenient wrapper and especially useful for cases when an indicator can be a null handle. Check how the latter is used in IGESToBRep_CurveAndSurface.cxx for example.

Designing your own indicator
Message_ProgressIndicator is an abstract class, so you must subclass it and redefine two pure virtual methods – Show() and UserBreak(). They define how progress indicator must be displayed and whether the user signaled that operation needs to be canceled. Let me show you an example of that in the next part.

To be continued...

No comments:

Post a Comment