diff --git a/src/Draw/Draw_ProgressIndicator.cxx b/src/Draw/Draw_ProgressIndicator.cxx index 2734d9951f..0b55eb74ef 100644 --- a/src/Draw/Draw_ProgressIndicator.cxx +++ b/src/Draw/Draw_ProgressIndicator.cxx @@ -28,24 +28,24 @@ IMPLEMENT_STANDARD_RTTIEXT(Draw_ProgressIndicator,Message_ProgressIndicator) //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(); } @@ -63,7 +63,8 @@ void Draw_ProgressIndicator::Reset() myShown = Standard_False; } myBreak = Standard_False; - myLastUpdate = myStartTime = 0; + myLastPosition = -1.; + myStartTime = 0; } //======================================================================= @@ -73,14 +74,22 @@ void Draw_ProgressIndicator::Reset() 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]; @@ -100,14 +109,18 @@ Standard_Boolean Draw_ProgressIndicator::Show(const Standard_Boolean force) 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;" diff --git a/src/Draw/Draw_ProgressIndicator.hxx b/src/Draw/Draw_ProgressIndicator.hxx index f176e5298f..bfb6710e4d 100644 --- a/src/Draw/Draw_ProgressIndicator.hxx +++ b/src/Draw/Draw_ProgressIndicator.hxx @@ -33,18 +33,15 @@ class Draw_ProgressIndicator : public Message_ProgressIndicator 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); @@ -85,8 +82,8 @@ private: Standard_Address myDraw; Standard_Boolean myShown; Standard_Boolean myBreak; - Standard_Integer myUpdateTime; - Standard_Size myLastUpdate; + Standard_Real myUpdateThreshold; + Standard_Real myLastPosition; Standard_Size myStartTime; }; diff --git a/src/Message/Message_ProgressIndicator.hxx b/src/Message/Message_ProgressIndicator.hxx index 369fed93b0..a9d90e2b48 100644 --- a/src/Message/Message_ProgressIndicator.hxx +++ b/src/Message/Message_ProgressIndicator.hxx @@ -33,50 +33,45 @@ class Message_ProgressIndicator; 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 { diff --git a/src/Message/Message_ProgressSentry.hxx b/src/Message/Message_ProgressSentry.hxx index f8114d7bd2..0e7a094a5d 100644 --- a/src/Message/Message_ProgressSentry.hxx +++ b/src/Message/Message_ProgressSentry.hxx @@ -37,6 +37,25 @@ class TCollection_HAsciiString; //! 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: diff --git a/src/OSD/OSD_Chronometer.cxx b/src/OSD/OSD_Chronometer.cxx index 73738dffac..699e9c2e11 100644 --- a/src/OSD/OSD_Chronometer.cxx +++ b/src/OSD/OSD_Chronometer.cxx @@ -205,7 +205,7 @@ void OSD_Chronometer::Reset () //======================================================================= void OSD_Chronometer::Restart () { - Stopped = Standard_True; + Reset(); Start(); } diff --git a/src/QABugs/QABugs_11.cxx b/src/QABugs/QABugs_11.cxx index 78b24ce412..f432ca06c0 100644 --- a/src/QABugs/QABugs_11.cxx +++ b/src/QABugs/QABugs_11.cxx @@ -4791,6 +4791,30 @@ Standard_Integer CR23403 (Draw_Interpretor& di, Standard_Integer argc, const cha 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"; @@ -4896,5 +4920,6 @@ void QABugs::Commands_11(Draw_Interpretor& theCommands) { 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; } diff --git a/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx b/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx index 22197809fb..fe5fe0e138 100644 --- a/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx +++ b/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx @@ -116,7 +116,8 @@ static Standard_Integer readstl(Draw_Interpretor& theDI, 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; diff --git a/tests/bugs/fclasses/bug28478 b/tests/bugs/fclasses/bug28478 new file mode 100644 index 0000000000..513f3f9cb7 --- /dev/null +++ b/tests/bugs/fclasses/bug28478 @@ -0,0 +1,24 @@ +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 diff --git a/tests/perf/fclasses/progress b/tests/perf/fclasses/progress new file mode 100644 index 0000000000..1c8df5e52a --- /dev/null +++ b/tests/perf/fclasses/progress @@ -0,0 +1,10 @@ +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"