Tests are added to control output and performance of progress indicator (bugs fclasses bug28478 and perf fclasses progress, respectively).
Implementation of class Draw_ProgressIndicator is improved to update indicator basing on achieved total progress (1% by default) instead of elapsed time since last update.
Method OSD_Chronometer::Restart() is fixed to actually reset the counter.
DRAW command readstl is improved to show progress indicator if configured (by command XProgress).
Description of class Message_ProgressIndicator is updated; code example is added in description of Message_ProgressSentry.
//function : Draw_ProgressIndicator
//purpose :
//=======================================================================
-Draw_ProgressIndicator::Draw_ProgressIndicator(const Draw_Interpretor &di,
- const Standard_Integer updateTime) :
- myTextMode ( DefaultTextMode() ),
- myGraphMode ( DefaultGraphMode() ),
- myDraw ( (Standard_Address)&di ),
- myShown ( Standard_False ),
- myBreak ( Standard_False ),
- myUpdateTime ( updateTime ),
- myLastUpdate ( 0 ), myStartTime ( 0 )
+Draw_ProgressIndicator::Draw_ProgressIndicator (const Draw_Interpretor &di, Standard_Real theUpdateThreshold)
+: myTextMode ( DefaultTextMode() ),
+ myGraphMode ( DefaultGraphMode() ),
+ myDraw ( (Standard_Address)&di ),
+ myShown ( Standard_False ),
+ myBreak ( Standard_False ),
+ myUpdateThreshold ( 0.01 * theUpdateThreshold ),
+ myLastPosition ( -1. ),
+ myStartTime ( 0 )
{
}
//=======================================================================
-//function : Destroy
+//function : ~Draw_ProgressIndicator
//purpose :
//=======================================================================
-void Draw_ProgressIndicator::Destroy()
+Draw_ProgressIndicator::~Draw_ProgressIndicator()
{
Reset();
}
myShown = Standard_False;
}
myBreak = Standard_False;
- myLastUpdate = myStartTime = 0;
+ myLastPosition = -1.;
+ myStartTime = 0;
}
//=======================================================================
Standard_Boolean Draw_ProgressIndicator::Show(const Standard_Boolean force)
{
- if ( ! myGraphMode && ! myTextMode ) return Standard_False;
- time_t aTimeT;
- time ( &aTimeT );
- Standard_Size aTime = (Standard_Size)aTimeT;
- if ( ! myStartTime ) myStartTime = aTime;
- if ( ! force && myUpdateTime >0 && aTime < myLastUpdate + myUpdateTime && GetPosition() < 1. )
+ if ( ! myGraphMode && ! myTextMode )
+ return Standard_False;
+
+ // remember time of the first call to Show as process start time
+ if ( ! myStartTime )
+ {
+ time_t aTimeT;
+ time ( &aTimeT );
+ myStartTime = (Standard_Size)aTimeT;
+ }
+
+ // unless show is forced, show updated state only if at least 1% progress has been reached since the last update
+ Standard_Real aPosition = GetPosition();
+ if ( ! force && aPosition < 1. && Abs (aPosition - myLastPosition) < myUpdateThreshold)
return Standard_False; // return if update interval has not elapsed
- myLastUpdate = aTime;
+ myLastPosition = aPosition;
// Prepare textual progress info
char text[2048];
scale.BaseToLocal ( locPos ), scale.GetMax() );
}
- // In addition, write elapsed/estimated/remaining time
- if ( GetPosition() > 0.01 ) {
- n += Sprintf ( &text[n], "\nElapsed/estimated time: %ld/%.0f sec",
- (long)(aTime - myStartTime), ( aTime - myStartTime ) / GetPosition() );
- }
-
// Show graphic progress bar
if ( myGraphMode ) {
+
+ // In addition, write elapsed/estimated/remaining time
+ if ( GetPosition() > 0.01 ) {
+ time_t aTimeT;
+ time ( &aTimeT );
+ Standard_Size aTime = (Standard_Size)aTimeT;
+ n += Sprintf ( &text[n], "\nElapsed/estimated time: %ld/%.0f sec",
+ (long)(aTime - myStartTime), ( aTime - myStartTime ) / GetPosition() );
+ }
+
if ( ! myShown ) {
char command[1024];
Sprintf ( command, "toplevel .xprogress -height 100 -width 410;"
public:
- //! Creates a progress indicator and remembers pointer to
- //! Draw_Interpretor
- //! The updateTime, if given, defines time interval between
- //! updates of the indicator (in seconds)
- Standard_EXPORT Draw_ProgressIndicator(const Draw_Interpretor& di, const Standard_Integer updateTime = 0);
+ //! Creates a progress indicator and remembers pointer to Draw_Interpretor
+ //!
+ //! @param theUpdateThreshold defines minimal progress (in percents) between
+ //! updates of the indicator (non-forced updates of the progress bar will be
+ //! disabled until that progress is reached since last update).
+ Standard_EXPORT Draw_ProgressIndicator(const Draw_Interpretor& di, Standard_Real theUpdateThreshold = 1.);
//! Destructor; calls Reset()
- Standard_EXPORT void Destroy();
-~Draw_ProgressIndicator()
-{
- Destroy();
-}
+ Standard_EXPORT ~Draw_ProgressIndicator();
//! Sets text output mode (on/off)
Standard_EXPORT void SetTextMode (const Standard_Boolean theTextMode);
Standard_Address myDraw;
Standard_Boolean myShown;
Standard_Boolean myBreak;
- Standard_Integer myUpdateTime;
- Standard_Size myLastUpdate;
+ Standard_Real myUpdateThreshold;
+ Standard_Real myLastPosition;
Standard_Size myStartTime;
};
DEFINE_STANDARD_HANDLE(Message_ProgressIndicator, Standard_Transient)
//! Defines abstract interface from program to the "user".
-//! That includes progress indication and user break mechanisms
+//! This includes progress indication and user break mechanisms.
//!
-//! The interface to progress indicator represents it as a scale
-//! for each range and step can be defined by the program that uses it.
+//! The process that uses the progress indicator interacts with it as
+//! with a scale whose range and step can be configured according to
+//! the nature of the process.
//! The scale can be made "infinite", which means it will grow
-//! non-linearly, end of scale will be approached asymptotically at
-//! infinite number of steps. In that case value of scale range
-//! gives a number of steps corresponding to position at 1/2 of scale.
+//! non-linearly, and end of scale will be approached asymptotically at
+//! infinite number of steps. In that case the range defines
+//! a number of steps corresponding to position at 1/2 of scale.
//! The current position can be either set directly (in a range from
//! current position to maximum scale value), or incremented step
//! by step.
//!
//! Progress indication mechanism is adapted for convenient
//! usage in hiererchical processes that require indication of
-//! progress at several (sub)levels of the process.
+//! progress at several levels of the process nesting.
//! For that purpose, it is possible to create restricted sub-scope of
-//! indication by specifying part of a current scale that is to be
+//! indication by specifying part of a current scale to be
//! used by the subprocess.
//! When subprocess works with progress indicator in the restricted
//! scope, it has the same interface to a scale, while actually it
//! deals only with part of the whole scale.
+//!
+//! The recommended way to implement progress indication in the algorithm
+//! is to use class Message_ProgressSentry that provides iterator-like
+//! interface for incrementing progress and opening nested scopes.
//!
//! NOTE:
//! Currently there is no support for concurrent progress
//! indicator that could be useful in multithreaded applications.
-//! The main reason for this is that such implementation would be
-//! too complex regarding forecasted lack of real need for such
-//! support.
-//! To support this it would require that ProgressScale keep its
-//! own position and take care of incrementing main ProgressIndicator
-//! in destructor. This would also require having cross-references
-//! between nested instances of ProgressScale, ie. potential
-//! problems with memory management.
-//! In case of need of concurrent progress indicator two things can
-//! be suggested: either creation of single spane with summary number
-//! of steps, or usage of infinite scale.
//!
-//! The user break is implemented as virtual function that might
-//! return True in case if break signal from the user is obtained.
+//! The user break is implemented as virtual function that should
+//! return True in case if break signal from the user is received.
//!
-//! The derived classes should take care of visualisation of the
+//! The derived class should take care of visualisation of the
//! progress indicator (e.g. show total position at the graphical bar,
-//! and/or print all scopes in text mode), and for implementation
-//! of user break mechanism (if defined).
+//! print scopes in text mode, or else), and for implementation
+//! of user break mechanism (if necessary).
+
class Message_ProgressIndicator : public Standard_Transient
{
//! check for user break
//! - Automatic scope closing in destructor
//! - Safe for NULL ProgressIndicator (just does nothing)
+//!
+//! Example of usage in nested process:
+//!
+//! @code{.cpp}
+//! Handle(Draw_ProgressIndicator) aProgress = ...;
+//!
+//! // Outer cycle
+//! Message_ProgressSentry anOuter (aProgress, "Outer", 0, nbOuter, 1);
+//! for (int i = 0; i < nbOuter && anOuter.More(); i++, anOuter.Next())
+//! {
+//! // Inner cycle
+//! Message_ProgressSentry anInner (aProgress, "Inner", 0, nbInner, 1);
+//! for (int j = 0; j < nbInner && anInner.More(); j++, anInner.Next())
+//! {
+//! // Cycle body
+//! }
+//! }
+//! @endcode
+
class Message_ProgressSentry
{
public:
//=======================================================================
void OSD_Chronometer::Restart ()
{
- Stopped = Standard_True;
+ Reset();
Start();
}
return 0;
}
+Standard_Integer OCC28478 (Draw_Interpretor& di, Standard_Integer argc, const char ** argv)
+{
+ Standard_Integer nbOuter = (argc > 1 ? Draw::Atoi(argv[1]) : 3);
+ Standard_Integer nbInner = (argc > 2 ? Draw::Atoi(argv[2]) : 2);
+
+ // test behavior of progress indicator when using nested scopes with names set by Sentry objects
+ Handle(Draw_ProgressIndicator) aProgress = new Draw_ProgressIndicator (di, 1);
+ aProgress->SetTextMode (Standard_True);
+
+ // Outer cycle
+ Message_ProgressSentry anOuter (aProgress, "Outer", 0, nbOuter, 1);
+ for (int i = 0; i < nbOuter && anOuter.More(); i++, anOuter.Next())
+ {
+ // Inner cycle
+ Message_ProgressSentry anInner (aProgress, "Inner", 0, nbInner, 1);
+ for (int j = 0; j < nbInner && anInner.More(); j++, anInner.Next())
+ {
+ // Cycle body
+ }
+ }
+
+ return 0;
+}
+
void QABugs::Commands_11(Draw_Interpretor& theCommands) {
const char *group = "QABugs";
theCommands.Add("OCC22558", "OCC22558 x_vec y_vec z_vec x_dir y_dir z_dit x_pnt y_pnt z_pnt", __FILE__, OCC22558, group);
theCommands.Add("CR23403", "CR23403 string", __FILE__, CR23403, group);
theCommands.Add("OCC23429", "OCC23429 res shape tool [appr]", __FILE__, OCC23429, group);
+ theCommands.Add("OCC28478", "OCC28478 [nb_outer=3 [nb_inner=2]: test progress indicator on nested cycles", __FILE__, OCC28478, group);
return;
}
strcmp("triangulation", theArgv[3]) == 0)
{
// Read STL file to the triangulation.
- Handle(Poly_Triangulation) aTriangulation = RWStl::ReadFile (theArgv[2]);
+ Handle(Draw_ProgressIndicator) aProgress = new Draw_ProgressIndicator (theDI, 1);
+ Handle(Poly_Triangulation) aTriangulation = RWStl::ReadFile (theArgv[2], aProgress);
TopoDS_Face aFace;
BRep_Builder aB;
--- /dev/null
+puts "# ============"
+puts "# 0028478: Scope Names Are Swallowed in Message_ProgressSentry Constructors"
+puts "# ============"
+puts ""
+puts "# Test output of progress indicator in text mode"
+
+pload QAcommands
+set out [OCC28478 3 2]
+
+set expected {
+ {Progress: 0% Outer: 1 / 3}
+ {Progress: 17% Outer: 1 / 3 Inner: 1 / 2}
+ {Progress: 33% Outer: 1 / 3 Inner: 2 / 2}
+ {Progress: 50% Outer: 2 / 3 Inner: 1 / 2}
+ {Progress: 67% Outer: 2 / 3 Inner: 2 / 2}
+ {Progress: 83% Outer: 3 / 3 Inner: 1 / 2}
+ {Progress: 100% Outer: 3 / 3 Inner: 2 / 2}
+}
+
+if { [string compare [string trim $out] [join $expected "\n"]] } {
+ puts "Error: output (see above) does not match expected one:"
+ puts "[join $expected "\n"]"
+ puts ""
+}
\ No newline at end of file
--- /dev/null
+puts "# ========"
+puts "# Measure performance of progress indicator on many empty cycles"
+puts "# ========"
+puts ""
+
+pload QAcommands
+
+chrono s restart
+OCC28478 10000 10000
+chrono s counter "100 M cycles of progress indicator"