diff --git a/CHANGELOG.md b/CHANGELOG.md index 299cf9c..31003ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.3.0] - 2021-02-28 +### Added +- Optional switch to completely disable the bar's progress and printing functionality via `IsActive`. If `false`, the progress bar is disabled. The default is `true`. + ## [3.2.0] - 2020-11-13 ### Added - Optional switch to override default MATLAB font in Windows by `OverrideDefaultFont` property. If `true` and while the bar is not released, MATLAB's code font will be changed programmatically to `Courier New` diff --git a/ProgressBar.m b/ProgressBar.m index 538ff8b..58ae852 100644 --- a/ProgressBar.m +++ b/ProgressBar.m @@ -45,7 +45,7 @@ properties (Constant) % Tag every timer with this to find it properly TIMER_TAG_NAME = 'ProgressBar'; - VERSION = '3.2.0'; + VERSION = '3.3.0'; end properties (Nontunable) @@ -68,6 +68,10 @@ end properties (Logical, Nontunable) + % Boolean whether to activate progress bar at all + % useful for non-interactive / batch / hpc usage + IsActive = true; + % Boolean whether to use Unicode symbols or ASCII hash symbols (i.e. #) UseUnicode = true; @@ -113,7 +117,7 @@ % The maximum length of the title string without banner cycling MAX_TITLE_LENGTH = 20; - + OVERRIDE_FONT_NAME = 'Courier New'; end @@ -236,54 +240,56 @@ end function [] = setupImpl(obj) - % get a new tic object - obj.TicObject = tic; - - % workaround for issue "Bar Gets Longer With Each Iteration" on windows systems - s = settings; - if obj.OverrideDefaultFont && ispc() - % store current font to reset the code font back to this value in the release() method - obj.CurrentFont = s.matlab.fonts.codefont.Name.ActiveValue; + if obj.IsActive + % get a new tic object + obj.TicObject = tic; - % change to Courier New which is shipped by every Windows distro since Windows 3.1 - s.matlab.fonts.codefont.Name.TemporaryValue = ob.OVERRIDE_FONT_NAME; - end - - % add a new timer object with the standard tag name and hide it - obj.TimerObject = timer(... - 'Tag', obj.TIMER_TAG_NAME, ... - 'ObjectVisibility', 'off' ... - ); - - % if the bar should not be printed in every iteration setup the - % timer to the desired update rate - if obj.HasFiniteUpdateRate - obj.setupTimer(); - end - - % if 'Total' is known setup the bar correspondingly and compute - % some constant values - if obj.HasTotalIterations - % initialize the progress bar and pre-compute some measures - obj.setupBar(); - obj.computeBlockFractions(); - end - - obj.CurrentTitleState = obj.Title; - if length(obj.Title) > obj.MAX_TITLE_LENGTH - obj.CurrentTitleState = [obj.CurrentTitleState, ' -- ']; - end - - % if the bar is used in a parallel setup start the timer right now - if obj.IsParallel - obj.startTimer(); - end - - % if this is a nested bar hit return - if obj.IsThisBarNested - fprintf(1, '\n'); + % workaround for issue "Bar Gets Longer With Each Iteration" on windows systems + s = settings; + if obj.OverrideDefaultFont && ispc() + % store current font to reset the code font back to this value in the release() method + obj.CurrentFont = s.matlab.fonts.codefont.Name.ActiveValue; + + % change to Courier New which is shipped by every Windows distro since Windows 3.1 + s.matlab.fonts.codefont.Name.TemporaryValue = ob.OVERRIDE_FONT_NAME; + end + + % add a new timer object with the standard tag name and hide it + obj.TimerObject = timer(... + 'Tag', obj.TIMER_TAG_NAME, ... + 'ObjectVisibility', 'off' ... + ); + + % if the bar should not be printed in every iteration setup the + % timer to the desired update rate + if obj.HasFiniteUpdateRate + obj.setupTimer(); + end + + % if 'Total' is known setup the bar correspondingly and compute + % some constant values + if obj.HasTotalIterations + % initialize the progress bar and pre-compute some measures + obj.setupBar(); + obj.computeBlockFractions(); + end + + obj.CurrentTitleState = obj.Title; + if length(obj.Title) > obj.MAX_TITLE_LENGTH + obj.CurrentTitleState = [obj.CurrentTitleState, ' -- ']; + end + + % if the bar is used in a parallel setup start the timer right now + if obj.IsParallel + obj.startTimer(); + end + + % if this is a nested bar hit return + if obj.IsThisBarNested + fprintf(1, '\n'); + end + obj.printProgressBar(); end - obj.printProgressBar(); end function [] = stepImpl(obj, stepSize, wasSuccessful, shouldPrintNextProgBar) @@ -338,38 +344,39 @@ {'scalar', 'binary', 'nonnan', 'nonempty'} ... ); - - % increment the iteration counter - obj.incrementIterationCounter(stepSize); - - % if the timer was stopped before, because no update was given, - % start it now again. - if ~obj.IsTimerRunning && obj.HasFiniteUpdateRate - obj.startTimer(); - end - - % if the iteration was not successful print a message saying so. - if ~wasSuccessful - infoMsg = sprintf('Iteration %i was not successful!', ... - obj.IterationCounter); - obj.printMessage(infoMsg, shouldPrintNextProgBar); - end - - % when the bar should be updated in every iteration, do this with - % each time calling update() - if ~obj.HasFiniteUpdateRate - obj.printProgressBar(); - end - - % stop the timer after the last iteration if an update rate is - % used. The first condition is needed to prevent the if-statement - % to fail if obj.Total is empty. This happens when no total number - % of iterations was passed / is known. - if ~isempty(obj.Total) ... - && obj.IterationCounter == obj.Total ... - && obj.HasFiniteUpdateRate + if obj.IsActive + % increment the iteration counter + obj.incrementIterationCounter(stepSize); - obj.stopTimer(); + % if the timer was stopped before, because no update was given, + % start it now again. + if ~obj.IsTimerRunning && obj.HasFiniteUpdateRate + obj.startTimer(); + end + + % if the iteration was not successful print a message saying so. + if ~wasSuccessful + infoMsg = sprintf('Iteration %i was not successful!', ... + obj.IterationCounter); + obj.printMessage(infoMsg, shouldPrintNextProgBar); + end + + % when the bar should be updated in every iteration, do this with + % each time calling update() + if ~obj.HasFiniteUpdateRate + obj.printProgressBar(); + end + + % stop the timer after the last iteration if an update rate is + % used. The first condition is needed to prevent the if-statement + % to fail if obj.Total is empty. This happens when no total number + % of iterations was passed / is known. + if ~isempty(obj.Total) ... + && obj.IterationCounter == obj.Total ... + && obj.HasFiniteUpdateRate + + obj.stopTimer(); + end end end @@ -482,7 +489,7 @@ else unitString = 'it'; - if obj.HasItPerSecBelow1 + if obj.HasItPerSecBelow1 fractionString = {'s', 'it'}; else fractionString = {'it', 's'}; @@ -857,4 +864,3 @@ function deleteAllTimers() end - diff --git a/README.md b/README.md index 1889dad..cdc6d8d 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,8 @@ Several projects exist on MATLAB's [File Exchange](https://www.mathworks.com/mat - [x] linear ETA estimate over all last iterations - [x] support parfor loops provided by the Parallel Computing Toolbox - [x] show s/it if it/sec < 1 -- [ ] have a template functionality like in [minibar](https://github.com/canassa/minibar). Maybe use `regexprep()`? +- [x] override MATLAB's default non-UTF font if `OverrideDefaultFont` is `true`. This will switch the font for the command line to `Courier New` for the lifetime of the bar. Default is `false` +- [x] disable the progress bar if `IsActive` is `false`. This will disable the functionality completely and can be used in situations in which the bar is not beneficial (e.g. if the bar is used in a sub-application of a processing cluster). Default is `true`. **Note**: diff --git a/demos/disableBar.m b/demos/disableBar.m new file mode 100644 index 0000000..d3fe9ca --- /dev/null +++ b/demos/disableBar.m @@ -0,0 +1,24 @@ +% This script will show how the bar can be programmatically disabled. +% +% Author: J.-A. Adrian (JA) +% + +clear; +close all; + +addpath('..'); + +numIterations = 25; + + +%% This Will Show the Bar +for iIteration = progress(1:numIterations) + pause(0.1); +end + + +%% This Will Disable the Bar +for iIteration = progress(1:numIterations, 'IsActive', false) + fprintf('New iteration...\n'); + pause(0.1); +end diff --git a/tests/ProgressBar_test.m b/tests/ProgressBar_test.m index 1198f17..292f6f8 100644 --- a/tests/ProgressBar_test.m +++ b/tests/ProgressBar_test.m @@ -204,6 +204,18 @@ function canRunWithoutUpdateTimer(testCase) testCase.verifyTrue(contains(firstBar, '1/2it')); testCase.verifyTrue(contains(secondBar, '2/2it')); end + + + function canBeDisabled(testCase) + unit = testCase.getUnit(2, 'IsActive', false); + + firstBar = evalc('unit([], [], [])'); + secondBar = evalc('unit([], [], [])'); + unit.release(); + + testCase.verifyEmpty(firstBar); + testCase.verifyEmpty(secondBar); + end end diff --git a/tests/progress_test.m b/tests/progress_test.m index 1518efa..4355ec2 100644 --- a/tests/progress_test.m +++ b/tests/progress_test.m @@ -68,6 +68,13 @@ function simpleCall(testCase) testCase.verifyTrue(contains(str, '100%')); testCase.verifyTrue(contains(str, '1/1it')); end + + + function disabledProgress(testCase) + str = evalc('for k = progress(1, ''IsActive'', false); end;'); + + testCase.verifyEmpty(str); + end end end