From f255fbf0e5d4fcba3ccb6497c70e001d6ff6089b Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Wed, 4 Nov 2020 21:03:36 +0100 Subject: [PATCH 01/30] Implement font override --- ProgressBar.m | 76 ++++++++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/ProgressBar.m b/ProgressBar.m index b109dd2..3e6b413 100644 --- a/ProgressBar.m +++ b/ProgressBar.m @@ -39,50 +39,6 @@ % state % % Author : J.-A. Adrian (JA) -% Date : 17-Jun-2016 16:08:45 -% - -% History: v1.0 working ProgressBar with and without knowledge of total -% number of iterations, 21-Jun-2016 (JA) -% v2.0 support for update rate, 21-Jun-2016 (JA) -% v2.1 colored progress bar, 22-Jun-2016 (JA) -% v2.2 support custom step size, 22-Jun-2016 (JA) -% v2.3 nested bars, 22-Jun-2016 (JA) -% v2.4 printMessage() and info when iteration was not -% successful, 23-Jun-2016 (JA) -% v2.5 support 'Bytes' as unit, 23-Jun-2016 (JA) -% v2.5.1 bug fixing, 23-Jun-2016 (JA) -% v2.6 timer stops when no updates arrive, 23-Jun-2016 (JA) -% v2.7 introduce progress loop via wrapper class, -% 23- Jun-2016 (JA) -% v2.7.1 bug fixing, 25-Jun-2016 (JA) -% v2.8 support ASCII symbols, 25-Jun-2016 (JA) -% v2.8.1 consider isdeployed -% v2.8.2 fix a bug concerning bar updating, 27-Jun-2016 (JA) -% v2.8.3 update documentation and demos, 27-Jun-2016 (JA) -% v2.8.4 update known issues -% v2.8.5 bug fixes -% v2.9 support of parallel parfor loops, 28-Jun-2016 (JA) -% v2.9.1 bug fixing (deploy mode) and optimization, -% 28-Jun-2016 (JA) -% v2.9.2 fix bug in updateParallel(), 29-Jun-2016 (JA) -% v2.9.3 fix bug where updateParallel() doesn't return the -% correct write-directory, 30-June-2016 (JA) -% v2.9.4 the directory of the worker aux. files can now be -% specified, 03-Jul-2016 (JA) -% v2.9.5 show s/it if it/s < 1 for easier overview, -% 10-Aug-2016 (JA) -% v2.9.6 default update rate is now 5 Hz, 10-Aug-2016 (JA) -% v2.9.7 remove commas after if, for, etc., improve stability a -% bit, 11-Oct-2016 (JA) -% v3.0.0 - refactor the class to be a MATLAB System Object -% - support a title banner if title longer than -% MaxTitleLength -% 02-May-2017 (JA) -% v3.1.0 timing initialization is now done in the object's setup phase. Previously, this -% was done in the constructor so time book keeping could have been awkward if -% step() methods wouldn't have been called immediately. Some private properties -% have been renamed. 03-11-2019 (JA) % @@ -112,8 +68,14 @@ end properties (Logical, Nontunable) + % Boolean whether to use Unicode symbols or ASCII hash symbols (i.e. #) UseUnicode = true; + + % Boolean whether the progress bar is to be used in parallel computing setup IsParallel = false; + + % Boolean whether to override Windows' monospaced font to cure "growing bar" syndrome + OverrideDefaultFont = false; end properties (Access = private) @@ -139,6 +101,8 @@ MaxBarWidth = 90; CurrentTitleState = ''; + + CurrentFont; end properties (Constant, Access = private) @@ -258,13 +222,9 @@ {'scalar', 'binary', 'nonnan', 'nonempty'} ... ); - assert(... - checkInputOfTotal(obj.Total) ... - ); + assert(checkInputOfTotal(obj.Total)); - assert(... - any(strcmpi(obj.Unit, {'Iterations', 'Bytes'})) ... - ); + assert(any(strcmpi(obj.Unit, {'Iterations', 'Bytes'}))); valFunStrings(obj.Title); valFunStrings(obj.WorkerDirectory); @@ -277,6 +237,16 @@ % 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; + + % change to Courier New which is shipped by every Windows distro since Windows 3.1 + s.matlab.fonts.codefont.Name.TemporaryValue = 'Courier New'; + end + % add a new timer object with the standard tag name and hide it obj.TimerObject = timer(... 'Tag', obj.TIMER_TAG_NAME, ... @@ -420,6 +390,12 @@ % delete the timer object delete(obj.TimerObject); + % restore previously used font + if obj.OverrideDefaultFont && ~isempty(obj.CurrentFont) + s = settings; + s.matlab.fonts.codefont.Name.TemporaryValue = obj.CurrentFont; + end + % if used in parallel processing delete all aux. files and clear % the persistent variables inside of updateParallel() if obj.IsParallel From 62a40b1b97b4675e962616c2921dd673692903a6 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Wed, 4 Nov 2020 21:03:42 +0100 Subject: [PATCH 02/30] Rename self to obj --- progress.m | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/progress.m b/progress.m index 7273476..218992d 100644 --- a/progress.m +++ b/progress.m @@ -37,12 +37,6 @@ % % % Author : J.-A. Adrian (JA) -% Date : 23-Jun-2016 19:24:50 -% - -% Version: v1.0 initial version, 23-Jun-2016 (JA) -% v1.1 rename variables and update documentation, -% 26-Jun-2016 (JA) % @@ -53,36 +47,36 @@ methods % Class Constructor - function self = progress(in, varargin) + function obj = progress(in, varargin) if ~nargin return; end - self.IterationList = in; + obj.IterationList = in; % pass all varargins to ProgressBar() - self.ProgressBar = ProgressBar(length(in), varargin{:}); + obj.ProgressBar = ProgressBar(length(in), varargin{:}); end % Class Destructor - function delete(self) + function delete(obj) % call the destructor of the ProgressBar() object - self.ProgressBar.release(); + obj.ProgressBar.release(); end - function [varargout] = subsref(self, S) + function [varargout] = subsref(obj, S) % This method implements the subsref method and only calls the update() % method of ProgressBar. The actual input 'S' is passed to the default - % subsref method of the class of self.IterationList. + % subsref method of the class of obj.IterationList. - self.ProgressBar.step([], [], []); - varargout = {subsref(self.IterationList, S)}; + obj.ProgressBar.step([], [], []); + varargout = {subsref(obj.IterationList, S)}; end - function [m, n] = size(self) + function [m, n] = size(obj) % This method implements the size() function for the progress() class. - [m, n] = size(self.IterationList); + [m, n] = size(obj.IterationList); end end end From ffcceb75713dbde87c121c94b4a540a7c0bf31ee Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Wed, 4 Nov 2020 21:18:01 +0100 Subject: [PATCH 03/30] Update license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0f3041e..22d796b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause -Copyright (c) 2016, Jens-Alrik Adrian +Copyright (c) 2020, Jens-Alrik Adrian All rights reserved. Redistribution and use in source and binary forms, with or without From f0bf896758c80f1eed56329374848b7b52ad394a Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Wed, 4 Nov 2020 21:18:06 +0100 Subject: [PATCH 04/30] Update README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1775b5e..12f7b6a 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,13 @@ MATLAB's speed to print to the command window is actually pretty low. If the upd ### The Bar Gets Longer With Each Iteration -There seems to be a problem with the default font `Monospaced` at least on Windows. If this behavior is problematic change the font for the command window to a different monospaced font, preferably with proper Unicode support. +There seems to be a problem with the default font `Monospaced` in Windows. If this behavior is problematic, change the font for the command window to a different monospaced font, preferably with proper Unicode support. + +If you do not want to or cannot change the font in the setting, you can set the class's `OverrideDefaultFont` to `true` while you are in the configuration phase. This will change MATLAB's coding font to `Courier New` for the duration in which the bar is alive (until the `release()` method is executed). After the object's lifetime, your previous font will be restored automatically. + +For convenience, this property can also be set in the `progress()` wrapper to always trigger for the wrapper if desired. + +Thanks [@GenosseFlosse](https://github.com/GenosseFlosse) for the fix! ### Strange Symbols in the Progress Bar From 2c79b28afd68f853e7f65d61f44df58d8904de2e Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 5 Nov 2020 19:45:43 +0100 Subject: [PATCH 05/30] Use more static methods --- ProgressBar.m | 1539 ++++++++++++++++++++++++------------------------- 1 file changed, 755 insertions(+), 784 deletions(-) diff --git a/ProgressBar.m b/ProgressBar.m index 3e6b413..a6448cd 100644 --- a/ProgressBar.m +++ b/ProgressBar.m @@ -1,884 +1,855 @@ classdef ProgressBar < matlab.System -%PROGRESSBAR A class to provide a convenient and useful progress bar -% ------------------------------------------------------------------------- -% This class mimics the design and some features of the TQDM -% (https://github.com/tqdm/tqdm) progress bar in python. All optional -% functionalities are set via name-value pairs in the constructor after the -% argument of the total numbers of iterations used in the progress (which -% can also be empty if unknown or even neglected if no name-value pairs are -% passed). The central class' method is 'step()' to increment the -% progress state of the object. -% -% Usage: pbar = ProgressBar() -% pbar = ProgressBar(total) -% pbar = ProgressBar(total, Name, Value) -% -% where 'total' is the total number of iterations. -% -% -% ProgressBar Properties: -% Total - the total number of iterations [default: []] -% Title - the progress bar's title shown in front [default: 'Processing'] -% Unit - the unit of the update process. Can either be 'Iterations' or -% 'Bytes' [default: 'Iterations'] -% UpdateRate - the progress bar's update rate in Hz. Defines the printing -% update interval [default: 5 Hz] -% -% -% ProgressBar Methods: -% ProgressBar - class constructor -% release - clean up and finish the progress bar's internal state -% printMessage - print some infos during the iterations. Messages get -% printed above the bar and the latter shifts one row down -% setup - not needed in a common application. Tiny helper function when -% setting up nested loops to print a parent bar before the first -% update occured. When the inner loop takes long, a nasty white -% space is shown in place of the parent bar until the first -% update takes place. This function can be used as a remedy. -% step - the central update method to increment the internal progress -% state -% -% Author : J.-A. Adrian (JA) -% - - -properties (Constant) - % Tag every timer with this to find it properly - TIMER_TAG_NAME = 'ProgressBar'; - VERSION = '3.1.0'; -end - -properties (Nontunable) - % Total number of iterations to compute progress and ETA - Total; - - % Titel of the progress bar if desired. Shown in front of the bar - Title = 'Processing'; - - % The visual printing rate in Hz. Default is 5 Hz - UpdateRate = 5; - - % The unit of each update. Can be either 'Iterations' or 'Bytes'. - % Default is 'Iterations'. - Unit = 'Iterations'; - - % Directory in which the worker binary files are being saved when in - % parallel mode. - WorkerDirectory = tempdir; -end - -properties (Logical, Nontunable) - % Boolean whether to use Unicode symbols or ASCII hash symbols (i.e. #) - UseUnicode = true; - - % Boolean whether the progress bar is to be used in parallel computing setup - IsParallel = false; - - % Boolean whether to override Windows' monospaced font to cure "growing bar" syndrome - OverrideDefaultFont = false; -end - -properties (Access = private) - Bar = ''; - IterationCounter = 0; - - NumWrittenCharacters = 0; - FractionMainBlock; - FractionSubBlock; - - HasTotalIterations = false; - HasBeenUpdated = false; - HasFiniteUpdateRate = true; - HasItPerSecBelow1 = false; - - BlockCharacters = getUnicodeSubBlocks(); - - IsTimerRunning = false; - - TicObject; - TimerObject; - - MaxBarWidth = 90; + %PROGRESSBAR A class to provide a convenient and useful progress bar + % ------------------------------------------------------------------------- + % This class mimics the design and some features of the TQDM + % (https://github.com/tqdm/tqdm) progress bar in python. All optional + % functionalities are set via name-value pairs in the constructor after the + % argument of the total numbers of iterations used in the progress (which + % can also be empty if unknown or even neglected if no name-value pairs are + % passed). The central class' method is 'step()' to increment the + % progress state of the object. + % + % Usage: pbar = ProgressBar() + % pbar = ProgressBar(total) + % pbar = ProgressBar(total, Name, Value) + % + % where 'total' is the total number of iterations. + % + % + % ProgressBar Properties: + % Total - the total number of iterations [default: []] + % Title - the progress bar's title shown in front [default: 'Processing'] + % Unit - the unit of the update process. Can either be 'Iterations' or + % 'Bytes' [default: 'Iterations'] + % UpdateRate - the progress bar's update rate in Hz. Defines the printing + % update interval [default: 5 Hz] + % + % + % ProgressBar Methods: + % ProgressBar - class constructor + % release - clean up and finish the progress bar's internal state + % printMessage - print some infos during the iterations. Messages get + % printed above the bar and the latter shifts one row down + % setup - not needed in a common application. Tiny helper function when + % setting up nested loops to print a parent bar before the first + % update occured. When the inner loop takes long, a nasty white + % space is shown in place of the parent bar until the first + % update takes place. This function can be used as a remedy. + % step - the central update method to increment the internal progress + % state + % + % Author : J.-A. Adrian (JA) + % - CurrentTitleState = ''; - - CurrentFont; -end - -properties (Constant, Access = private) - % The number of sub blocks in one main block of width of a character. - % HTML 'left blocks' go in eigths -> 8 sub blocks in one main block - NumSubBlocks = 8; - % The number of characters the title string should shift each cycle - NumCharactersShift = 3; + properties (Constant) + % Tag every timer with this to find it properly + TIMER_TAG_NAME = 'ProgressBar'; + VERSION = '3.1.0'; + end - % The maximum length of the title string without banner cycling - MaxTitleLength = 20; -end - -properties (Access = private, Dependent) - IsThisBarNested; -end - - - - -methods - % Class Constructor - function [obj] = ProgressBar(total, varargin) - if nargin - obj.Total = total; - - obj.setProperties(nargin-1, varargin{:}); - end + properties (Nontunable) + % Total number of iterations to compute progress and ETA + Total; - if ~isempty(obj.Total) - obj.HasTotalIterations = true; - end - if isinf(obj.UpdateRate) - obj.HasFiniteUpdateRate = false; - end + % Titel of the progress bar if desired. Shown in front of the bar + Title = 'Processing'; - % check if prog. bar runs in deployed mode and if so, switch to - % ASCII symbols and a smaller bar width - if isdeployed - obj.UseUnicode = false; - obj.MaxBarWidth = 72; - end + % The visual printing rate in Hz. Default is 5 Hz + UpdateRate = 5; + + % The unit of each update. Can be either 'Iterations' or 'Bytes'. + % Default is 'Iterations'. + Unit = 'Iterations'; - % setup ASCII symbols if desired - if ~obj.UseUnicode - obj.BlockCharacters = getAsciiSubBlocks(); - end + % Directory in which the worker binary files are being saved when in + % parallel mode. + WorkerDirectory = tempdir; end - function [] = printMessage(obj, message, shouldPrintNextProgBar) - %PRINTMESSAGE class method to print a message while prog bar running - %---------------------------------------------------------------------- - % This method lets the user print a message during the processing. A - % normal fprintf() or disp() would break the bar so this method can be - % used to print information about iterations or debug infos. - % - % Usage: obj.printMessage(obj, message) - % obj.printMessage(obj, message, shouldPrintNextProgBar) - % - % Input: --------- - % message - the message that should be printed to screen - % shouldPrintNextProgBar - Boolean to define wether to - % immidiately print another prog. bar - % after print the success message. Can - % be usefule when every iteration takes - % a long time and a white space appears - % where the progress bar used to be. - % [default: shouldPrintNextProgBar = false] - % + properties (Logical, Nontunable) + % Boolean whether to use Unicode symbols or ASCII hash symbols (i.e. #) + UseUnicode = true; - % input parsing and validation - narginchk(2, 3); + % Boolean whether the progress bar is to be used in parallel computing setup + IsParallel = false; - if nargin < 3 || isempty(shouldPrintNextProgBar) - shouldPrintNextProgBar = false; - end - validateattributes(shouldPrintNextProgBar, ... - {'logical', 'numeric'}, ... - {'scalar', 'binary', 'nonempty', 'nonnan'} ... - ); + % Boolean whether to override Windows' monospaced font to cure "growing bar" syndrome + OverrideDefaultFont = false; + end + + properties (Access = private) + Bar = ''; + IterationCounter = 0; - % remove the current prog bar - fprintf(1, backspace(obj.NumWrittenCharacters)); + NumWrittenCharacters = 0; + FractionMainBlock; + FractionSubBlock; - % print the message and break the line - fprintf(1, '\t'); - fprintf(1, message); - fprintf(1, '\n'); + HasTotalIterations = false; + HasBeenUpdated = false; + HasFiniteUpdateRate = true; + HasItPerSecBelow1 = false; - % reset the number of written characters - obj.NumWrittenCharacters = 0; + BlockCharacters; - % if the next prog bar should be printed immideately do this - if shouldPrintNextProgBar - obj.printProgressBar(); - end + IsTimerRunning = false; + + TicObject; + TimerObject; + + MaxBarWidth = 90; + + CurrentTitleState = ''; + + CurrentFont; end - function [yesNo] = get.IsThisBarNested(obj) - % If there are more than one timer object with our tag, the current - % bar must be nested - yesNo = length(obj.getTimerList()) > 1; + properties (Constant, Access = private) + % The number of sub blocks in one main block of width of a character. + % HTML 'left blocks' go in eigths -> 8 sub blocks in one main block + NumSubBlocks = 8; + + % The number of characters the title string should shift each cycle + NumCharactersShift = 3; + + % The maximum length of the title string without banner cycling + MaxTitleLength = 20; end -end - - -methods (Access = protected) - function [] = validatePropertiesImpl(obj) - valFunStrings = @(in) validateattributes(in, {'char'}, {'nonempty'}); - valFunNumeric = @(in) validateattributes(in, ... - {'numeric'}, ... - {'scalar', 'positive', 'real', 'nonempty', 'nonnan'} ... - ); - valFunBoolean = @(in) validateattributes(in, ... - {'logical', 'numeric'}, ... - {'scalar', 'binary', 'nonnan', 'nonempty'} ... - ); - - assert(checkInputOfTotal(obj.Total)); - - assert(any(strcmpi(obj.Unit, {'Iterations', 'Bytes'}))); - - valFunStrings(obj.Title); - valFunStrings(obj.WorkerDirectory); - valFunNumeric(obj.UpdateRate); - valFunBoolean(obj.UseUnicode); - valFunBoolean(obj.IsParallel); + + properties (Access = private, Dependent) + IsThisBarNested; 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; - - % change to Courier New which is shipped by every Windows distro since Windows 3.1 - s.matlab.fonts.codefont.Name.TemporaryValue = 'Courier New'; - 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.MaxTitleLength - obj.CurrentTitleState = [obj.CurrentTitleState, ' -- ']; + + + + methods + % Class Constructor + function [obj] = ProgressBar(total, varargin) + if nargin + obj.Total = total; + + obj.setProperties(nargin-1, varargin{:}); + end + + if ~isempty(obj.Total) + obj.HasTotalIterations = true; + end + if isinf(obj.UpdateRate) + obj.HasFiniteUpdateRate = false; + end + + % check if prog. bar runs in deployed mode and if so, switch to + % ASCII symbols and a smaller bar width + if isdeployed + obj.UseUnicode = false; + obj.MaxBarWidth = 72; + end + + % setup ASCII symbols if desired + if obj.UseUnicode + obj.BlockCharacters = ProgressBar.getUnicodeSubBlocks(); + else + obj.BlockCharacters = ProgressBar.getAsciiSubBlocks(); + end end - % if the bar is used in a parallel setup start the timer right now - if obj.IsParallel - obj.startTimer(); + function [] = printMessage(obj, message, shouldPrintNextProgBar) + %PRINTMESSAGE class method to print a message while prog bar running + %---------------------------------------------------------------------- + % This method lets the user print a message during the processing. A + % normal fprintf() or disp() would break the bar so this method can be + % used to print information about iterations or debug infos. + % + % Usage: obj.printMessage(obj, message) + % obj.printMessage(obj, message, shouldPrintNextProgBar) + % + % Input: --------- + % message - the message that should be printed to screen + % shouldPrintNextProgBar - Boolean to define wether to + % immidiately print another prog. bar + % after print the success message. Can + % be usefule when every iteration takes + % a long time and a white space appears + % where the progress bar used to be. + % [default: shouldPrintNextProgBar = false] + % + + % input parsing and validation + narginchk(2, 3); + + if nargin < 3 || isempty(shouldPrintNextProgBar) + shouldPrintNextProgBar = false; + end + validateattributes(shouldPrintNextProgBar, ... + {'logical', 'numeric'}, ... + {'scalar', 'binary', 'nonempty', 'nonnan'} ... + ); + + % remove the current prog bar + fprintf(1, ProgressBar.backspace(obj.NumWrittenCharacters)); + + % print the message and break the line + fprintf(1, '\t'); + fprintf(1, message); + fprintf(1, '\n'); + + % reset the number of written characters + obj.NumWrittenCharacters = 0; + + % if the next prog bar should be printed immideately do this + if shouldPrintNextProgBar + obj.printProgressBar(); + end end - % if this is a nested bar hit return - if obj.IsThisBarNested - fprintf(1, '\n'); + function [yesNo] = get.IsThisBarNested(obj) + % If there are more than one timer object with our tag, the current + % bar must be nested + yesNo = length(obj.getTimerList()) > 1; end - obj.printProgressBar(); end - function [] = stepImpl(obj, stepSize, wasSuccessful, shouldPrintNextProgBar) - %STEPIMPL class method to increment the object's progress state - %---------------------------------------------------------------------- - % This method is the central update function in the loop to indicate - % the increment of the progress. Pass empty arrays for each input - % argument if default is desired. - % - % Usage: obj.step(stepSize, wasSuccessful, shouldPrintNextProgBar) - % - % Input: --------- - % stepSize - the size of the progress step when the method is - % called. This can be used to pass the number of - % processed bytes when using 'Bytes' as units. - % [default: stepSize = 1] - % wasSuccessful - Boolean to provide information about the - % success of an individual iteration. If you pass - % a 'false' a message will be printed stating the - % current iteration was not successful. - % [default: wasSuccessful = true] - % shouldPrintNextProgBar - Boolean to define wether to - % immidiately print another prog. bar - % after print the success message. Can - % be useful when every iteration takes - % a long time and a white space appears - % where the progress bar used to be. - % [default: shouldPrintNextProgBar = false] - % - - % input parsing and validating - if isempty(shouldPrintNextProgBar) - shouldPrintNextProgBar = false; - end - if isempty(wasSuccessful) - wasSuccessful = true; - end - if isempty(stepSize) - stepSize = 1; - end - - validateattributes(stepSize, ... - {'numeric'}, ... - {'scalar', 'positive', 'real', 'nonnan', 'finite', 'nonempty'} ... - ); - validateattributes(wasSuccessful, ... - {'logical', 'numeric'}, ... - {'scalar', 'binary', 'nonnan', 'nonempty'} ... - ); - validateattributes(shouldPrintNextProgBar, ... - {'logical', 'numeric'}, ... - {'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(); + + methods (Access = protected) + function [] = validatePropertiesImpl(obj) + valFunStrings = @(in) validateattributes(in, {'char'}, {'nonempty'}); + valFunNumeric = @(in) validateattributes(in, ... + {'numeric'}, ... + {'scalar', 'positive', 'real', 'nonempty', 'nonnan'} ... + ); + valFunBoolean = @(in) validateattributes(in, ... + {'logical', 'numeric'}, ... + {'scalar', 'binary', 'nonnan', 'nonempty'} ... + ); + + assert(ProgressBar.checkInputOfTotal(obj.Total)); + + assert(any(strcmpi(obj.Unit, {'Iterations', 'Bytes'}))); + + valFunStrings(obj.Title); + valFunStrings(obj.WorkerDirectory); + valFunNumeric(obj.UpdateRate); + valFunBoolean(obj.UseUnicode); + valFunBoolean(obj.IsParallel); 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); + 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; + + % change to Courier New which is shipped by every Windows distro since Windows 3.1 + s.matlab.fonts.codefont.Name.TemporaryValue = 'Courier New'; + 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.MaxTitleLength + 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 - % when the bar should be updated in every iteration, do this with - % each time calling update() - if ~obj.HasFiniteUpdateRate - obj.printProgressBar(); + function [] = stepImpl(obj, stepSize, wasSuccessful, shouldPrintNextProgBar) + %STEPIMPL class method to increment the object's progress state + %---------------------------------------------------------------------- + % This method is the central update function in the loop to indicate + % the increment of the progress. Pass empty arrays for each input + % argument if default is desired. + % + % Usage: obj.step(stepSize, wasSuccessful, shouldPrintNextProgBar) + % + % Input: --------- + % stepSize - the size of the progress step when the method is + % called. This can be used to pass the number of + % processed bytes when using 'Bytes' as units. + % [default: stepSize = 1] + % wasSuccessful - Boolean to provide information about the + % success of an individual iteration. If you pass + % a 'false' a message will be printed stating the + % current iteration was not successful. + % [default: wasSuccessful = true] + % shouldPrintNextProgBar - Boolean to define wether to + % immidiately print another prog. bar + % after print the success message. Can + % be useful when every iteration takes + % a long time and a white space appears + % where the progress bar used to be. + % [default: shouldPrintNextProgBar = false] + % + + % input parsing and validating + if isempty(shouldPrintNextProgBar) + shouldPrintNextProgBar = false; + end + if isempty(wasSuccessful) + wasSuccessful = true; + end + if isempty(stepSize) + stepSize = 1; + end + + validateattributes(stepSize, ... + {'numeric'}, ... + {'scalar', 'positive', 'real', 'nonnan', 'finite', 'nonempty'} ... + ); + validateattributes(wasSuccessful, ... + {'logical', 'numeric'}, ... + {'scalar', 'binary', 'nonnan', 'nonempty'} ... + ); + validateattributes(shouldPrintNextProgBar, ... + {'logical', 'numeric'}, ... + {'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 + + obj.stopTimer(); + end 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 + function [] = releaseImpl(obj) + % stop the timer + if obj.IsTimerRunning + obj.stopTimer(); + end + + if obj.IsThisBarNested + % when this prog bar was nested, remove it from the command + % line and get back to the end of the parent bar. + % +1 due to the line break + fprintf(1, ProgressBar.backspace(obj.NumWrittenCharacters + 1)); + elseif obj.IterationCounter && ~obj.IsThisBarNested + % when a non-nested progress bar has been plotted, hit return + fprintf(1, '\n'); + end + + % delete the timer object + delete(obj.TimerObject); - obj.stopTimer(); + % restore previously used font + if obj.OverrideDefaultFont && ~isempty(obj.CurrentFont) + s = settings; + s.matlab.fonts.codefont.Name.TemporaryValue = obj.CurrentFont; + end + + % if used in parallel processing delete all aux. files and clear + % the persistent variables inside of updateParallel() + if obj.IsParallel + files = ProgressBar.findWorkerFiles(obj.WorkerDirectory); + + if ~isempty(files) + delete(files{:}); + end + + clear updateParallel; + + % rest some time to not flood the screen with the parent bar + pause(0.1); + end end end - function [] = releaseImpl(obj) - % stop the timer - if obj.IsTimerRunning - obj.stopTimer(); + + + methods (Access = ?ProgressBar_test) + function [] = computeBlockFractions(obj) + % Compute the progress percentage of a single main and a single sub + % block + obj.FractionMainBlock = 1 / length(obj.Bar); + obj.FractionSubBlock = obj.FractionMainBlock / obj.NumSubBlocks; end - if obj.IsThisBarNested - % when this prog bar was nested, remove it from the command - % line and get back to the end of the parent bar. - % +1 due to the line break - fprintf(1, backspace(obj.NumWrittenCharacters + 1)); - elseif obj.IterationCounter && ~obj.IsThisBarNested - % when a non-nested progress bar has been plotted, hit return - fprintf(1, '\n'); + + function [] = setupBar(obj) + % Set up the growing bar part of the printed line by computing the + % width of it + + [~, preBarFormat, postBarFormat] = obj.returnFormatString(); + + % insert worst case inputs to get (almost) maximum length of bar + preBar = sprintf(... + preBarFormat, ... + blanks(min(length(obj.CurrentTitleState), obj.MaxTitleLength)), ... + 100 ... + ); + postBar = sprintf(... + postBarFormat, ... + obj.Total, ... + obj.Total, ... + 10, 60, 60, 10, 60, 60, 1e2 ... + ); + + lenBar = obj.MaxBarWidth - length(preBar) - length(postBar); + + obj.Bar = blanks(lenBar); end - % delete the timer object - delete(obj.TimerObject); - % restore previously used font - if obj.OverrideDefaultFont && ~isempty(obj.CurrentFont) - s = settings; - s.matlab.fonts.codefont.Name.TemporaryValue = obj.CurrentFont; - end - - % if used in parallel processing delete all aux. files and clear - % the persistent variables inside of updateParallel() - if obj.IsParallel - files = findWorkerFiles(obj.WorkerDirectory); + function [] = printProgressBar(obj) + % This method removes the old and prints the current bar to the screen + % and saves the number of written characters for the next iteration - if ~isempty(files) - delete(files{:}); - end + % remove old previous bar + fprintf(1, ProgressBar.backspace(obj.NumWrittenCharacters)); - clear updateParallel; + formatString = obj.returnFormatString(); + argumentList = obj.returnArgumentList(); - % rest some time to not flood the screen with the parent bar - pause(0.1); + % print new bar + obj.NumWrittenCharacters = fprintf(1, ... + formatString, ... + argumentList{:} ... + ); end - end -end - - - - -methods (Access = private) - function [] = computeBlockFractions(obj) - % Compute the progress percentage of a single main and a single sub - % block - obj.FractionMainBlock = 1 / length(obj.Bar); - obj.FractionSubBlock = obj.FractionMainBlock / obj.NumSubBlocks; - end - - - - function [] = setupBar(obj) - % Set up the growing bar part of the printed line by computing the - % width of it - - [~, preBarFormat, postBarFormat] = obj.returnFormatString(); - - % insert worst case inputs to get (almost) maximum length of bar - preBar = sprintf(... - preBarFormat, ... - blanks(min(length(obj.CurrentTitleState), obj.MaxTitleLength)), ... - 100 ... - ); - postBar = sprintf(... - postBarFormat, ... - obj.Total, ... - obj.Total, ... - 10, 60, 60, 10, 60, 60, 1e2 ... - ); - - lenBar = obj.MaxBarWidth - length(preBar) - length(postBar); - - obj.Bar = blanks(lenBar); - end - - - - - function [] = printProgressBar(obj) - % This method removes the old and prints the current bar to the screen - % and saves the number of written characters for the next iteration - - % remove old previous bar - fprintf(1, backspace(obj.NumWrittenCharacters)); - formatString = obj.returnFormatString(); - argumentList = obj.returnArgumentList(); - % print new bar - obj.NumWrittenCharacters = fprintf(1, ... - formatString, ... - argumentList{:} ... - ); - end - - - - - - function [format, preString, postString] = returnFormatString(obj) - % This method returns the format string for the fprintf() function in - % printProgressBar() - - % use the correct units - if strcmp(obj.Unit, 'Bytes') - if obj.HasItPerSecBelow1 - unitStrings = {'K', 's', 'KB'}; + function [format, preString, postString] = returnFormatString(obj) + % This method returns the format string for the fprintf() function in + % printProgressBar() + + % use the correct units + if strcmp(obj.Unit, 'Bytes') + if obj.HasItPerSecBelow1 + unitStrings = {'K', 's', 'KB'}; + else + unitStrings = {'K', 'KB', 's'}; + end else - unitStrings = {'K', 'KB', 's'}; + if obj.HasItPerSecBelow1 + unitStrings = {'', 's', 'it'}; + else + unitStrings = {'', 'it', 's'}; + end end - else - if obj.HasItPerSecBelow1 - unitStrings = {'', 's', 'it'}; + + % consider a growing bar if the total number of iterations is known + % and consider a title if one is given. + if obj.HasTotalIterations + preString = '%s: %03.0f%% '; + + centerString = '|%s|'; + + postString = ... + [' %i', unitStrings{1}, '/%i', unitStrings{1}, ... + ' [%02.0f:%02.0f:%02.0f<%02.0f:%02.0f:%02.0f, %.2f ', ... + unitStrings{2}, '/', unitStrings{3}, ']']; + + format = [preString, centerString, postString]; else - unitStrings = {'', 'it', 's'}; + preString = ''; + postString = ''; + + format = ['%s: %i', unitStrings{2}, ... + ' [%02.0f:%02.0f:%02.0f, %.2f ', unitStrings{2}, '/', ... + unitStrings{3}, ']']; end end + function [argList] = returnArgumentList(obj) + % This method returns the argument list as a cell array for the + % fprintf() function in printProgressBar() + + % elapsed time (ET) + thisTimeSec = toc(obj.TicObject); + etHoursMinsSecs = ProgressBar.convertTime(thisTimeSec); + + % mean iterations per second counted from the start + iterationsPerSecond = obj.IterationCounter / thisTimeSec; + + if iterationsPerSecond < 1 + iterationsPerSecond = 1 / iterationsPerSecond; + obj.HasItPerSecBelow1 = true; + else + obj.HasItPerSecBelow1 = false; + end + + + % consider the correct units + scaledIteration = obj.IterationCounter; + scaledTotal = obj.Total; + if strcmp(obj.Unit, 'Bytes') + % let's show KB + scaledIteration = round(scaledIteration / 1000); + scaledTotal = round(scaledTotal / 1000); + iterationsPerSecond = iterationsPerSecond / 1000; + end + + if obj.HasTotalIterations + % 1 : Title + % 2 : progress percent + % 3 : progBar string + % 4 : iterationCounter + % 5 : Total + % 6 : ET.hours + % 7 : ET.minutes + % 8 : ET.seconds + % 9 : ETA.hours + % 10: ETA.minutes + % 11: ETA.seconds + % 12: it/s + + % estimated time of arrival (ETA) + [etaHoursMinsSecs] = obj.estimateETA(thisTimeSec); + + if obj.IterationCounter + % usual case -> the iteration counter is > 0 + barString = obj.getCurrentBar; + else + % if startMethod() calls this method return the empty bar + barString = obj.Bar; + end + + argList = { + obj.CurrentTitleState(... + 1:min(length(obj.Title), obj.MaxTitleLength) ... + ), ... + floor(obj.IterationCounter / obj.Total * 100), ... + barString, ... + scaledIteration, ... + scaledTotal, ... + etHoursMinsSecs(1), ... + etHoursMinsSecs(2), ... + etHoursMinsSecs(3), ... + etaHoursMinsSecs(1), ... + etaHoursMinsSecs(2), ... + etaHoursMinsSecs(3), ... + iterationsPerSecond ... + }; + else + % 1: Title + % 2: iterationCounter + % 3: ET.hours + % 4: ET.minutes + % 5: ET.seconds + % 6: it/s + + argList = { + obj.CurrentTitleState(... + 1:min(length(obj.Title), obj.MaxTitleLength) ... + ), ..., ... + scaledIteration, ... + etHoursMinsSecs(1), ... + etHoursMinsSecs(2), ... + etHoursMinsSecs(3), ... + iterationsPerSecond ... + }; + end + + % cycle the bar's title + obj.updateCurrentTitle(); + end + - % consider a growing bar if the total number of iterations is known - % and consider a title if one is given. - if obj.HasTotalIterations - preString = '%s: %03.0f%% '; + function [barString] = getCurrentBar(obj) + % This method constructs the growing bar part of the printed line by + % indexing the correct part of the blank bar and getting either a + % Unicode or ASCII symbol. + + % set up the bar and the current progress as a ratio + lenBar = length(obj.Bar); + currProgress = obj.IterationCounter / obj.Total; - centerString = '|%s|'; + % index of the current main block + thisMainBlock = min(ceil(currProgress / obj.FractionMainBlock), lenBar); - postString = ... - [' %i', unitStrings{1}, '/%i', unitStrings{1}, ... - ' [%02.0f:%02.0f:%02.0f<%02.0f:%02.0f:%02.0f, %.2f ', ... - unitStrings{2}, '/', unitStrings{3}, ']']; + % index of the current sub block + continuousBlockIndex = ceil(currProgress / obj.FractionSubBlock); + thisSubBlock = mod(continuousBlockIndex - 1, obj.NumSubBlocks) + 1; - format = [preString, centerString, postString]; - else - preString = ''; - postString = ''; + % fix for non-full last blocks when steps are large: make them full + obj.Bar(1:max(thisMainBlock-1, 0)) = ... + repmat(obj.BlockCharacters(end), 1, thisMainBlock - 1); - format = ['%s: %i', unitStrings{2}, ... - ' [%02.0f:%02.0f:%02.0f, %.2f ', unitStrings{2}, '/', ... - unitStrings{3}, ']']; + % return a full bar in the last iteration or update the current + % main block + if obj.IterationCounter == obj.Total + obj.Bar = repmat(obj.BlockCharacters(end), 1, lenBar); + else + obj.Bar(thisMainBlock) = obj.BlockCharacters(thisSubBlock); + end + + barString = obj.Bar; end - end - - - - - function [argList] = returnArgumentList(obj) - % This method returns the argument list as a cell array for the - % fprintf() function in printProgressBar() - % elapsed time (ET) - thisTimeSec = toc(obj.TicObject); - etHoursMinsSecs = convertTime(thisTimeSec); - - % mean iterations per second counted from the start - iterationsPerSecond = obj.IterationCounter / thisTimeSec; - - if iterationsPerSecond < 1 - iterationsPerSecond = 1 / iterationsPerSecond; - obj.HasItPerSecBelow1 = true; - else - obj.HasItPerSecBelow1 = false; + + function [etaHoursMinsSecs] = estimateETA(obj, elapsedTime) + % This method estimates linearly the remaining time + + % the current progress as ratio + progress = obj.IterationCounter / obj.Total; + + % the remaining seconds + remainingSeconds = elapsedTime * ((1 / progress) - 1); + + % convert seconds to hours:mins:seconds + etaHoursMinsSecs = ProgressBar.convertTime(remainingSeconds); end - % consider the correct units - scaledIteration = obj.IterationCounter; - scaledTotal = obj.Total; - if strcmp(obj.Unit, 'Bytes') - % let's show KB - scaledIteration = round(scaledIteration / 1000); - scaledTotal = round(scaledTotal / 1000); - iterationsPerSecond = iterationsPerSecond / 1000; + function [] = setupTimer(obj) + % This method initializes the timer object if an upate rate is used + + obj.TimerObject.BusyMode = 'drop'; + obj.TimerObject.ExecutionMode = 'fixedSpacing'; + + if ~obj.IsParallel + obj.TimerObject.TimerFcn = @(~, ~) obj.timerCallback(); + obj.TimerObject.StopFcn = @(~, ~) obj.timerCallback(); + else + obj.TimerObject.TimerFcn = @(~, ~) obj.timerCallbackParallel(); + obj.TimerObject.StopFcn = @(~, ~) obj.timerCallbackParallel(); + end + updatePeriod = round(1 / obj.UpdateRate * 1000) / 1000; + obj.TimerObject.Period = updatePeriod; end - if obj.HasTotalIterations - % 1 : Title - % 2 : progress percent - % 3 : progBar string - % 4 : iterationCounter - % 5 : Total - % 6 : ET.hours - % 7 : ET.minutes - % 8 : ET.seconds - % 9 : ETA.hours - % 10: ETA.minutes - % 11: ETA.seconds - % 12: it/s - - % estimated time of arrival (ETA) - [etaHoursMinsSecs] = obj.estimateETA(thisTimeSec); - - if obj.IterationCounter - % usual case -> the iteration counter is > 0 - barString = obj.getCurrentBar; + + function [] = timerCallback(obj) + % This method is the timer callback. If an update came in between the + % last printing and now print a new prog bar, else stop the timer and + % wait. + if obj.HasBeenUpdated + obj.printProgressBar(); else - % if startMethod() calls this method return the empty bar - barString = obj.Bar; + obj.stopTimer(); end - argList = { - obj.CurrentTitleState(... - 1:min(length(obj.Title), obj.MaxTitleLength) ... - ), ... - floor(obj.IterationCounter / obj.Total * 100), ... - barString, ... - scaledIteration, ... - scaledTotal, ... - etHoursMinsSecs(1), ... - etHoursMinsSecs(2), ... - etHoursMinsSecs(3), ... - etaHoursMinsSecs(1), ... - etaHoursMinsSecs(2), ... - etaHoursMinsSecs(3), ... - iterationsPerSecond ... - }; - else - % 1: Title - % 2: iterationCounter - % 3: ET.hours - % 4: ET.minutes - % 5: ET.seconds - % 6: it/s - - argList = { - obj.CurrentTitleState(... - 1:min(length(obj.Title), obj.MaxTitleLength) ... - ), ..., ... - scaledIteration, ... - etHoursMinsSecs(1), ... - etHoursMinsSecs(2), ... - etHoursMinsSecs(3), ... - iterationsPerSecond ... - }; + obj.HasBeenUpdated = false; end - % cycle the bar's title - obj.updateCurrentTitle(); - end - - - - - function [barString] = getCurrentBar(obj) - % This method constructs the growing bar part of the printed line by - % indexing the correct part of the blank bar and getting either a - % Unicode or ASCII symbol. - - % set up the bar and the current progress as a ratio - lenBar = length(obj.Bar); - currProgress = obj.IterationCounter / obj.Total; - - % index of the current main block - thisMainBlock = min(ceil(currProgress / obj.FractionMainBlock), lenBar); - - % index of the current sub block - continuousBlockIndex = ceil(currProgress / obj.FractionSubBlock); - thisSubBlock = mod(continuousBlockIndex - 1, obj.NumSubBlocks) + 1; - - % fix for non-full last blocks when steps are large: make them full - obj.Bar(1:max(thisMainBlock-1, 0)) = ... - repmat(obj.BlockCharacters(end), 1, thisMainBlock - 1); - - % return a full bar in the last iteration or update the current - % main block - if obj.IterationCounter == obj.Total - obj.Bar = repmat(obj.BlockCharacters(end), 1, lenBar); - else - obj.Bar(thisMainBlock) = obj.BlockCharacters(thisSubBlock); + + function [] = timerCallbackParallel(obj) + % find the aux. worker files + [files, numFiles] = ProgressBar.findWorkerFiles(obj.WorkerDirectory); + + % if none have been written yet just print a progressbar and return + if ~numFiles + obj.printProgressBar(); + + return; + end + + % read the status in every file + results = zeros(numFiles, 1); + for iFile = 1:numFiles + fid = fopen(files{iFile}, 'rb'); + + if fid > 0 + results(iFile) = fread(fid, 1, 'uint64'); + fclose(fid); + end + end + + % the sum of all files should be the current iteration + obj.IterationCounter = sum(results); + + % print the progress bar + obj.printProgressBar(); + + % if total is known and we are at the end stop the timer + if ~isempty(obj.Total) && obj.IterationCounter == obj.Total + obj.stopTimer(); + end end - barString = obj.Bar; - end - - - - - function [etaHoursMinsSecs] = estimateETA(obj, elapsedTime) - % This method estimates linearly the remaining time - % the current progress as ratio - progress = obj.IterationCounter / obj.Total; + function [] = startTimer(obj) + % This method starts the timer object and updates the status bool + + start(obj.TimerObject); + obj.IsTimerRunning = true; + end - % the remaining seconds - remainingSeconds = elapsedTime * ((1 / progress) - 1); - % convert seconds to hours:mins:seconds - etaHoursMinsSecs = convertTime(remainingSeconds); - end - - - - - function [] = setupTimer(obj) - % This method initializes the timer object if an upate rate is used - - obj.TimerObject.BusyMode = 'drop'; - obj.TimerObject.ExecutionMode = 'fixedSpacing'; - - if ~obj.IsParallel - obj.TimerObject.TimerFcn = @(~, ~) obj.timerCallback(); - obj.TimerObject.StopFcn = @(~, ~) obj.timerCallback(); - else - obj.TimerObject.TimerFcn = @(~, ~) obj.timerCallbackParallel(); - obj.TimerObject.StopFcn = @(~, ~) obj.timerCallbackParallel(); + function [] = stopTimer(obj) + % This method stops the timer object and updates the status bool + + stop(obj.TimerObject); + obj.IsTimerRunning = false; end - updatePeriod = round(1 / obj.UpdateRate * 1000) / 1000; - obj.TimerObject.Period = updatePeriod; - end - - - - function [] = timerCallback(obj) - % This method is the timer callback. If an update came in between the - % last printing and now print a new prog bar, else stop the timer and - % wait. - if obj.HasBeenUpdated - obj.printProgressBar(); - else - obj.stopTimer(); - end - - obj.HasBeenUpdated = false; - end - - - - - function [] = timerCallbackParallel(obj) - % find the aux. worker files - [files, numFiles] = findWorkerFiles(obj.WorkerDirectory); - % if none have been written yet just print a progressbar and return - if ~numFiles - obj.printProgressBar(); + + function [] = incrementIterationCounter(obj, stepSize) + % This method increments the iteration counter and updates the status + % bool - return; + obj.IterationCounter = obj.IterationCounter + stepSize; + + obj.HasBeenUpdated = true; end - % read the status in every file - results = zeros(numFiles, 1); - for iFile = 1:numFiles - fid = fopen(files{iFile}, 'rb'); + + function [list] = getTimerList(obj) + % This function returns the list of all hidden timers which are tagged + % with our default tag - if fid > 0 - results(iFile) = fread(fid, 1, 'uint64'); - fclose(fid); - end + list = timerfindall('Tag', obj.TIMER_TAG_NAME); end - % the sum of all files should be the current iteration - obj.IterationCounter = sum(results); - % print the progress bar - obj.printProgressBar(); - - % if total is known and we are at the end stop the timer - if ~isempty(obj.Total) && obj.IterationCounter == obj.Total - obj.stopTimer(); + function [] = updateCurrentTitle(obj) + strTitle = obj.CurrentTitleState; + + if length(strTitle) > obj.MaxTitleLength + strTitle = circshift(strTitle, -obj.NumCharactersShift); + + obj.CurrentTitleState = strTitle; + end end -end - - - - - function [] = startTimer(obj) - % This method starts the timer object and updates the status bool - - start(obj.TimerObject); - obj.IsTimerRunning = true; end - - - - function [] = stopTimer(obj) - % This method stops the timer object and updates the status bool + methods (Static) + function deleteAllTimers() + delete(timerfindall('Tag', ProgressBar.TIMER_TAG_NAME)); + end - stop(obj.TimerObject); - obj.IsTimerRunning = false; - end - - - - - function [] = incrementIterationCounter(obj, stepSize) - % This method increments the iteration counter and updates the status - % bool - obj.IterationCounter = obj.IterationCounter + stepSize; + function [blocks] = getUnicodeSubBlocks() + % This function returns the HTML 'left blocks' to construct the growing bar. The HTML + % 'left blocks' range from 1 to 8 excluding the 'space'. + + blocks = [ + char(9615); + char(9614); + char(9613); + char(9612); + char(9611); + char(9610); + char(9609); + char(9608); + ]; + end + + function [blocks] = getAsciiSubBlocks() + % This function returns the ASCII number signs (hashes) to construct the growing bar. + % The HTML 'left blocks' range from 1 to 8 excluding the 'space'. + + blocks = repmat('#', 1, 8); + end - obj.HasBeenUpdated = true; - end - - - - - function [list] = getTimerList(obj) - % This function returns the list of all hidden timers which are tagged - % with our default tag - list = timerfindall('Tag', obj.TIMER_TAG_NAME); - end - - - function [] = updateCurrentTitle(obj) - strTitle = obj.CurrentTitleState; + function [str] = backspace(numChars) + % This function returns the desired numbers of backspaces to delete characters from the + % current line + + str = repmat(sprintf('\b'), 1, numChars); + end + - if length(strTitle) > obj.MaxTitleLength - strTitle = circshift(strTitle, -obj.NumCharactersShift); + function [hoursMinsSecs] = convertTime(secondsIn) + % This fast implementation to convert seconds to hours:mins:seconds using mod() stems + % from http://stackoverflow.com/a/21233409 - obj.CurrentTitleState = strTitle; + hoursMinsSecs = floor(mod(secondsIn, [0, 3600, 60]) ./ [3600, 60, 1]); + end + + + function [yesNo] = checkInputOfTotal(total) + % This function is the input checker of the main constructor argument 'total'. It is ok + % if it's empty but if not it must obey validateattributes. + + isTotalEmpty = isempty(total); + + if isTotalEmpty + yesNo = isTotalEmpty; + return; + else + yesNo = ~isTotalEmpty; + validateattributes(total, ... + {'numeric'}, ... + {'scalar', 'integer', 'positive', 'real', 'nonnan', 'finite'} ... + ); + end + end + + function [files, numFiles] = findWorkerFiles(workerDir) + % This function returns file names and the number of files that were written by the + % updateParallel() function if the prog. bar is used in a parallel setup. + % + % Input: workerDir - directory where the aux. files of the worker are saved + % + + [pattern] = updateParallel(); + + files = dir(fullfile(workerDir, pattern)); + files = {files.name}; + + files = cellfun(... + @(filename) fullfile(workerDir, filename), ... + files, ... + 'uni', false ... + ); + numFiles = length(files); end end + end -methods (Static) - function deleteAllTimers() - delete(timerfindall('Tag', ProgressBar.TIMER_TAG_NAME)); - end -end - -end - - - -function [blocks] = getUnicodeSubBlocks() -% This function returns the HTML 'left blocks' to construct the growing -% bar. The HTML 'left blocks' range from 1 to 8 excluding the 'space'. - -blocks = [ - char(9615); - char(9614); - char(9613); - char(9612); - char(9611); - char(9610); - char(9609); - char(9608); - ]; -end - -function [blocks] = getAsciiSubBlocks() -% This function returns the ASCII number signs (hashes) to construct the -% growing bar. The HTML 'left blocks' range from 1 to 8 excluding the -% 'space'. - -blocks = repmat('#', 1, 8); -end - - -function [str] = backspace(numChars) -% This function returns the desired numbers of backspaces to delete -% characters from the current line - -str = repmat(sprintf('\b'), 1, numChars); -end - - -function [hoursMinsSecs] = convertTime(secondsIn) -% This fast implementation to convert seconds to hours:mins:seconds using -% mod() stems from http://stackoverflow.com/a/21233409 - -hoursMinsSecs = floor(mod(secondsIn, [0, 3600, 60]) ./ [3600, 60, 1]); -end - - -function [yesNo] = checkInputOfTotal(total) -% This function is the input checker of the main constructor argument -% 'total'. It is ok if it's empty but if not it must obey -% validateattributes. - -isTotalEmpty = isempty(total); - -if isTotalEmpty - yesNo = isTotalEmpty; - return; -else - yesNo = ~isTotalEmpty; - validateattributes(total, ... - {'numeric'}, ... - {'scalar', 'integer', 'positive', 'real', 'nonnan', 'finite'} ... - ); -end -end - -function [files, numFiles] = findWorkerFiles(workerDir) -% This function returns file names and the number of files that were -% written by the updateParallel() function if the prog. bar is used in a -% parallel setup. -% -% Input: workerDir - directory where the aux. files of the worker are saved -% - -[pattern] = updateParallel(); - -files = dir(fullfile(workerDir, pattern)); -files = {files.name}; - -files = cellfun(... - @(filename) fullfile(workerDir, filename), ... - files, ... - 'uni', false ... - ); -numFiles = length(files); -end From 492a23278cfabc5692d943b2cc9ae4d28a21cf1f Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 5 Nov 2020 19:59:58 +0100 Subject: [PATCH 06/30] First testcase --- ProgressBar_test.m | 65 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/ProgressBar_test.m b/ProgressBar_test.m index ad681e2..dd3ea4a 100644 --- a/ProgressBar_test.m +++ b/ProgressBar_test.m @@ -1,26 +1,63 @@ -% ProgressBar test file to be run by the MATLAB unit test function -% 'runtests.m' +classdef ProgressBar_test < matlab.unittest.TestCase +%PROGRESSBAR_TEST Unit test for ProgressBar.m +% ------------------------------------------------------------------------- +% Run it by calling 'runtests()' +% or specifically 'runtests('ProgressBar_test')' % % Author : J.-A. Adrian (JA) -% Date : 26-Jun-2016 19:30:27 +% Date : 05-Nov-2020 19:26:43 % -clear; -close all; -fileList = dir(fullfile('demos', '*.m')); -fileNames = {fileList.name}.'; -fileNames = cellfun(@(x) fullfile('demos', x), fileNames, 'uni', false); +properties (Constant) + DEFAULT_SEED = 123; +end + +properties + UnitName = "ProgressBar"; + + Seed; +end + + +methods (TestClassSetup) + function setClassRng(testCase) + testCase.Seed = rng(); + testCase.addTeardown(@rng, testCase.Seed); + + rng(testCase.DEFAULT_SEED); + end +end + +methods (TestMethodSetup) + function setMethodRng(testCase) + rng(testCase.DEFAULT_SEED); + end +end + -%% run the demo files to ensure that they don't throw error -for iDemoFile = 1:length(fileList) - run(fileNames{iDemoFile}); +methods (Test) + function testDeleteAllTimers(testCase) + unit = testCase.getUnit(1); + + tagName = unit.TIMER_TAG_NAME; + timer('Tag', tagName); + testCase.verifyNotEmpty(timerfindall('Tag', tagName)); + + unit.deleteAllTimers(); + testCase.verifyEmpty(timerfindall('Tag', tagName)); + end end -%% be sure that no timer objects are left -timerObjects = timerfindall('Tag', 'ProgressBar'); -assert(isempty(timerObjects)); +methods + function [unit] = getUnit(testCase, len) + unitHandle = str2func(testCase.UnitName); + unit = unitHandle(len); + end +end + +end From 5f3bc52e9b4d0c1718f37238dd78802092d23d89 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 5 Nov 2020 20:21:07 +0100 Subject: [PATCH 07/30] Next tests --- ProgressBar.m | 16 ++++++++-------- ProgressBar_test.m | 24 ++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/ProgressBar.m b/ProgressBar.m index a6448cd..06b8607 100644 --- a/ProgressBar.m +++ b/ProgressBar.m @@ -776,14 +776,14 @@ function deleteAllTimers() % 'left blocks' range from 1 to 8 excluding the 'space'. blocks = [ - char(9615); - char(9614); - char(9613); - char(9612); - char(9611); - char(9610); - char(9609); - char(9608); + char(9615), ... + char(9614), ... + char(9613), ... + char(9612), ... + char(9611), ... + char(9610), ... + char(9609), ... + char(9608) ... ]; end diff --git a/ProgressBar_test.m b/ProgressBar_test.m index dd3ea4a..8010d7a 100644 --- a/ProgressBar_test.m +++ b/ProgressBar_test.m @@ -38,8 +38,8 @@ function setMethodRng(testCase) methods (Test) - function testDeleteAllTimers(testCase) - unit = testCase.getUnit(1); + function testTimerDeletion(testCase) + unit = testCase.getUnit(); tagName = unit.TIMER_TAG_NAME; timer('Tag', tagName); @@ -48,12 +48,32 @@ function testDeleteAllTimers(testCase) unit.deleteAllTimers(); testCase.verifyEmpty(timerfindall('Tag', tagName)); end + + + function testUnicodeBlocks(testCase) + unit = testCase.getUnit(); + + blocks = unit.getUnicodeSubBlocks(); + testCase.verifyEqual(blocks, '▏▎▍▌▋▊▉█'); + end + + + function testAsciiBlocks(testCase) + unit = testCase.getUnit(); + + blocks = unit.getAsciiSubBlocks(); + testCase.verifyEqual(blocks, '########'); + end end methods function [unit] = getUnit(testCase, len) + if nargin < 2 || isempty(len) + len = []; + end + unitHandle = str2func(testCase.UnitName); unit = unitHandle(len); end From c03f13d753bbc2235d4d4ef575789de82f930bf0 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 5 Nov 2020 21:40:21 +0100 Subject: [PATCH 08/30] Finish static methods --- ProgressBar.m | 5 ++--- ProgressBar_test.m | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/ProgressBar.m b/ProgressBar.m index 06b8607..7adff54 100644 --- a/ProgressBar.m +++ b/ProgressBar.m @@ -811,17 +811,15 @@ function deleteAllTimers() end - function [yesNo] = checkInputOfTotal(total) + function [isTotalEmpty] = checkInputOfTotal(total) % This function is the input checker of the main constructor argument 'total'. It is ok % if it's empty but if not it must obey validateattributes. isTotalEmpty = isempty(total); if isTotalEmpty - yesNo = isTotalEmpty; return; else - yesNo = ~isTotalEmpty; validateattributes(total, ... {'numeric'}, ... {'scalar', 'integer', 'positive', 'real', 'nonnan', 'finite'} ... @@ -829,6 +827,7 @@ function deleteAllTimers() end end + function [files, numFiles] = findWorkerFiles(workerDir) % This function returns file names and the number of files that were written by the % updateParallel() function if the prog. bar is used in a parallel setup. diff --git a/ProgressBar_test.m b/ProgressBar_test.m index 8010d7a..49d6a13 100644 --- a/ProgressBar_test.m +++ b/ProgressBar_test.m @@ -64,6 +64,58 @@ function testAsciiBlocks(testCase) blocks = unit.getAsciiSubBlocks(); testCase.verifyEqual(blocks, '########'); end + + + function testBackspaces(testCase) + unit = testCase.getUnit(); + + backspaces = unit.backspace(3); + testCase.verifyEqual(backspaces, sprintf('\b\b\b')); + end + + + function testTimeConversion(testCase) + unit = testCase.getUnit(); + + testCase.verifyEqual(unit.convertTime(0), [0, 0, 0]); + testCase.verifyEqual(unit.convertTime(30), [0, 0, 30]); + testCase.verifyEqual(unit.convertTime(60), [0, 1, 0]); + testCase.verifyEqual(unit.convertTime(60*60), [1, 0, 0]); + end + + + function checkBarLengthInput(testCase) + unit = testCase.getUnit(); + + testCase.verifyEqual(unit.checkInputOfTotal([]), true); + testCase.verifyEqual(unit.checkInputOfTotal(10), false); + + testCase.verifyError(@() unit.checkInputOfTotal('char'), 'MATLAB:invalidType'); + testCase.verifyError(@() unit.checkInputOfTotal(-1), 'MATLAB:expectedPositive'); + testCase.verifyError(@() unit.checkInputOfTotal([1, 1]), 'MATLAB:expectedScalar'); + testCase.verifyError(@() unit.checkInputOfTotal(1j), 'MATLAB:expectedInteger'); + testCase.verifyError(@() unit.checkInputOfTotal(1.5), 'MATLAB:expectedInteger'); + testCase.verifyError(@() unit.checkInputOfTotal(inf), 'MATLAB:expectedInteger'); + testCase.verifyError(@() unit.checkInputOfTotal(nan), 'MATLAB:expectedInteger'); + end + + + function findWorkerFiles(testCase) + unit = testCase.getUnit(); + + pattern = updateParallel(); + testCase.assertEmpty(dir(pattern)); + + workerFilename = [pattern(1:end-1), 'test']; + fid = fopen(workerFilename, 'w'); + fclose(fid); + + foundFiles = unit.findWorkerFiles(pwd()); + testCase.verifyEqual(length(foundFiles), 1); + testCase.verifyEqual(foundFiles, {fullfile(pwd(), workerFilename)}); + + delete(workerFilename); + end end From 3fb58eede9376572cf3132a994102b153c6c12ab Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 5 Nov 2020 21:40:30 +0100 Subject: [PATCH 09/30] Update docstring --- updateParallel.m | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/updateParallel.m b/updateParallel.m index 79063b5..e013a4a 100644 --- a/updateParallel.m +++ b/updateParallel.m @@ -38,13 +38,7 @@ % % % Author: J.-A. Adrian (JA) -% Date : 28-Jun-2016 16:52 -% - -% History: v0.1 initial version, 28-Jun-2016 (JA) -% v1.0 the worker directory can be specified and will not be -% returned if called w/o input arguments, 03-Jul-2016 (JA) -% +% % some constants From d45ea3b12fdf3476738b8d163e57cc36117f6ea2 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 5 Nov 2020 21:43:00 +0100 Subject: [PATCH 10/30] Fix indenting --- ProgressBar_test.m | 221 ++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 111 deletions(-) diff --git a/ProgressBar_test.m b/ProgressBar_test.m index 49d6a13..fe2a2f6 100644 --- a/ProgressBar_test.m +++ b/ProgressBar_test.m @@ -1,135 +1,134 @@ classdef ProgressBar_test < matlab.unittest.TestCase -%PROGRESSBAR_TEST Unit test for ProgressBar.m -% ------------------------------------------------------------------------- -% Run it by calling 'runtests()' -% or specifically 'runtests('ProgressBar_test')' -% -% Author : J.-A. Adrian (JA) -% Date : 05-Nov-2020 19:26:43 -% - - -properties (Constant) - DEFAULT_SEED = 123; -end - -properties - UnitName = "ProgressBar"; - - Seed; -end - - -methods (TestClassSetup) - function setClassRng(testCase) - testCase.Seed = rng(); - testCase.addTeardown(@rng, testCase.Seed); - - rng(testCase.DEFAULT_SEED); - end -end - -methods (TestMethodSetup) - function setMethodRng(testCase) - rng(testCase.DEFAULT_SEED); - end -end - - - -methods (Test) - function testTimerDeletion(testCase) - unit = testCase.getUnit(); - - tagName = unit.TIMER_TAG_NAME; - timer('Tag', tagName); - testCase.verifyNotEmpty(timerfindall('Tag', tagName)); - - unit.deleteAllTimers(); - testCase.verifyEmpty(timerfindall('Tag', tagName)); - end + %PROGRESSBAR_TEST Unit test for ProgressBar.m + % ------------------------------------------------------------------------- + % Run it by calling 'runtests()' + % or specifically 'runtests('ProgressBar_test')' + % + % Author : J.-A. Adrian (JA) + % - function testUnicodeBlocks(testCase) - unit = testCase.getUnit(); - - blocks = unit.getUnicodeSubBlocks(); - testCase.verifyEqual(blocks, '▏▎▍▌▋▊▉█'); + properties (Constant) + DEFAULT_SEED = 123; end - - function testAsciiBlocks(testCase) - unit = testCase.getUnit(); + properties + UnitName = "ProgressBar"; - blocks = unit.getAsciiSubBlocks(); - testCase.verifyEqual(blocks, '########'); + Seed; end - function testBackspaces(testCase) - unit = testCase.getUnit(); - - backspaces = unit.backspace(3); - testCase.verifyEqual(backspaces, sprintf('\b\b\b')); + methods (TestClassSetup) + function setClassRng(testCase) + testCase.Seed = rng(); + testCase.addTeardown(@rng, testCase.Seed); + + rng(testCase.DEFAULT_SEED); + end end - - function testTimeConversion(testCase) - unit = testCase.getUnit(); - - testCase.verifyEqual(unit.convertTime(0), [0, 0, 0]); - testCase.verifyEqual(unit.convertTime(30), [0, 0, 30]); - testCase.verifyEqual(unit.convertTime(60), [0, 1, 0]); - testCase.verifyEqual(unit.convertTime(60*60), [1, 0, 0]); + methods (TestMethodSetup) + function setMethodRng(testCase) + rng(testCase.DEFAULT_SEED); + end end - function checkBarLengthInput(testCase) - unit = testCase.getUnit(); + + methods (Test) + function testTimerDeletion(testCase) + unit = testCase.getUnit(); + + tagName = unit.TIMER_TAG_NAME; + timer('Tag', tagName); + testCase.verifyNotEmpty(timerfindall('Tag', tagName)); + + unit.deleteAllTimers(); + testCase.verifyEmpty(timerfindall('Tag', tagName)); + end - testCase.verifyEqual(unit.checkInputOfTotal([]), true); - testCase.verifyEqual(unit.checkInputOfTotal(10), false); - testCase.verifyError(@() unit.checkInputOfTotal('char'), 'MATLAB:invalidType'); - testCase.verifyError(@() unit.checkInputOfTotal(-1), 'MATLAB:expectedPositive'); - testCase.verifyError(@() unit.checkInputOfTotal([1, 1]), 'MATLAB:expectedScalar'); - testCase.verifyError(@() unit.checkInputOfTotal(1j), 'MATLAB:expectedInteger'); - testCase.verifyError(@() unit.checkInputOfTotal(1.5), 'MATLAB:expectedInteger'); - testCase.verifyError(@() unit.checkInputOfTotal(inf), 'MATLAB:expectedInteger'); - testCase.verifyError(@() unit.checkInputOfTotal(nan), 'MATLAB:expectedInteger'); - end - - - function findWorkerFiles(testCase) - unit = testCase.getUnit(); + function testUnicodeBlocks(testCase) + unit = testCase.getUnit(); + + blocks = unit.getUnicodeSubBlocks(); + testCase.verifyEqual(blocks, '▏▎▍▌▋▊▉█'); + end - pattern = updateParallel(); - testCase.assertEmpty(dir(pattern)); - workerFilename = [pattern(1:end-1), 'test']; - fid = fopen(workerFilename, 'w'); - fclose(fid); + function testAsciiBlocks(testCase) + unit = testCase.getUnit(); + + blocks = unit.getAsciiSubBlocks(); + testCase.verifyEqual(blocks, '########'); + end - foundFiles = unit.findWorkerFiles(pwd()); - testCase.verifyEqual(length(foundFiles), 1); - testCase.verifyEqual(foundFiles, {fullfile(pwd(), workerFilename)}); - delete(workerFilename); - end -end - - - -methods - function [unit] = getUnit(testCase, len) - if nargin < 2 || isempty(len) - len = []; + function testBackspaces(testCase) + unit = testCase.getUnit(); + + backspaces = unit.backspace(3); + testCase.verifyEqual(backspaces, sprintf('\b\b\b')); + end + + + function testTimeConversion(testCase) + unit = testCase.getUnit(); + + testCase.verifyEqual(unit.convertTime(0), [0, 0, 0]); + testCase.verifyEqual(unit.convertTime(30), [0, 0, 30]); + testCase.verifyEqual(unit.convertTime(60), [0, 1, 0]); + testCase.verifyEqual(unit.convertTime(60*60), [1, 0, 0]); + end + + + function checkBarLengthInput(testCase) + unit = testCase.getUnit(); + + testCase.verifyEqual(unit.checkInputOfTotal([]), true); + testCase.verifyEqual(unit.checkInputOfTotal(10), false); + + testCase.verifyError(@() unit.checkInputOfTotal('char'), 'MATLAB:invalidType'); + testCase.verifyError(@() unit.checkInputOfTotal(-1), 'MATLAB:expectedPositive'); + testCase.verifyError(@() unit.checkInputOfTotal([1, 1]), 'MATLAB:expectedScalar'); + testCase.verifyError(@() unit.checkInputOfTotal(1j), 'MATLAB:expectedInteger'); + testCase.verifyError(@() unit.checkInputOfTotal(1.5), 'MATLAB:expectedInteger'); + testCase.verifyError(@() unit.checkInputOfTotal(inf), 'MATLAB:expectedInteger'); + testCase.verifyError(@() unit.checkInputOfTotal(nan), 'MATLAB:expectedInteger'); end - unitHandle = str2func(testCase.UnitName); - unit = unitHandle(len); + + function findWorkerFiles(testCase) + unit = testCase.getUnit(); + + pattern = updateParallel(); + testCase.assertEmpty(dir(pattern)); + + workerFilename = [pattern(1:end-1), 'test']; + fid = fopen(workerFilename, 'w'); + fclose(fid); + + foundFiles = unit.findWorkerFiles(pwd()); + testCase.verifyEqual(length(foundFiles), 1); + testCase.verifyEqual(foundFiles, {fullfile(pwd(), workerFilename)}); + + delete(workerFilename); + end end -end - - + + + + methods + function [unit] = getUnit(testCase, len) + if nargin < 2 || isempty(len) + len = []; + end + + unitHandle = str2func(testCase.UnitName); + unit = unitHandle(len); + end + end + + end From 4ee85ce10ab38084926f5ee2c00e1bd83e8b2ae0 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 5 Nov 2020 23:20:32 +0100 Subject: [PATCH 11/30] First essential bar tests --- ProgressBar.m | 40 +++++++++++++++------------ ProgressBar_test.m | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 17 deletions(-) diff --git a/ProgressBar.m b/ProgressBar.m index 7adff54..157840e 100644 --- a/ProgressBar.m +++ b/ProgressBar.m @@ -413,11 +413,8 @@ pause(0.1); end end - end - - - - methods (Access = ?ProgressBar_test) + + function [] = computeBlockFractions(obj) % Compute the progress percentage of a single main and a single sub % block @@ -475,16 +472,20 @@ % use the correct units if strcmp(obj.Unit, 'Bytes') + unitString = 'K'; + if obj.HasItPerSecBelow1 - unitStrings = {'K', 's', 'KB'}; + fractionString = {'s', 'KB'}; else - unitStrings = {'K', 'KB', 's'}; + fractionString = {'KB', 's'}; end else - if obj.HasItPerSecBelow1 - unitStrings = {'', 's', 'it'}; + unitString = 'it'; + + if obj.HasItPerSecBelow1 + fractionString = {'s', 'it'}; else - unitStrings = {'', 'it', 's'}; + fractionString = {'it', 's'}; end end @@ -496,18 +497,21 @@ centerString = '|%s|'; postString = ... - [' %i', unitStrings{1}, '/%i', unitStrings{1}, ... + [ + ' %i/%i', unitString, ... ' [%02.0f:%02.0f:%02.0f<%02.0f:%02.0f:%02.0f, %.2f ', ... - unitStrings{2}, '/', unitStrings{3}, ']']; + fractionString{1}, '/', fractionString{2}, ']' + ]; format = [preString, centerString, postString]; else preString = ''; postString = ''; - format = ['%s: %i', unitStrings{2}, ... - ' [%02.0f:%02.0f:%02.0f, %.2f ', unitStrings{2}, '/', ... - unitStrings{3}, ']']; + format = [ + '%s: %i', unitString, ' [%02.0f:%02.0f:%02.0f, %.2f ', ... + fractionString{1}, '/', fractionString{2}, ']' + ]; end end @@ -533,7 +537,7 @@ % consider the correct units scaledIteration = obj.IterationCounter; - scaledTotal = obj.Total; + scaledTotal = obj.Total; if strcmp(obj.Unit, 'Bytes') % let's show KB scaledIteration = round(scaledIteration / 1000); @@ -811,19 +815,21 @@ function deleteAllTimers() end - function [isTotalEmpty] = checkInputOfTotal(total) + function [isOk] = checkInputOfTotal(total) % This function is the input checker of the main constructor argument 'total'. It is ok % if it's empty but if not it must obey validateattributes. isTotalEmpty = isempty(total); if isTotalEmpty + isOk = true; return; else validateattributes(total, ... {'numeric'}, ... {'scalar', 'integer', 'positive', 'real', 'nonnan', 'finite'} ... ); + isOk = true; end end diff --git a/ProgressBar_test.m b/ProgressBar_test.m index fe2a2f6..1a2999e 100644 --- a/ProgressBar_test.m +++ b/ProgressBar_test.m @@ -34,6 +34,12 @@ function setMethodRng(testCase) end end + methods (TestMethodTeardown) + function deleteRogueTimers(~) + delete(timerfindall('Tag', ProgressBar.TIMER_TAG_NAME)); + end + end + methods (Test) @@ -115,6 +121,69 @@ function findWorkerFiles(testCase) delete(workerFilename); end + + + function barHasDefaults(testCase) + unit = testCase.getUnit(); + + testCase.verifyEqual(unit.TIMER_TAG_NAME, 'ProgressBar'); + testCase.verifyEmpty(unit.Total); + testCase.verifyEqual(unit.UpdateRate, 5); + testCase.verifyEqual(unit.Unit, 'Iterations'); + testCase.verifyTrue(unit.UseUnicode); + testCase.verifyFalse(unit.IsParallel); + testCase.verifyFalse(unit.OverrideDefaultFont); + end + + + function canSetBarTotal(testCase) + unit = testCase.getUnit(10); + + testCase.verifyEqual(unit.Total, 10); + end + + + function canPrintSimpleBar(testCase) + unit = testCase.getUnit(); + + firstBar = evalc('unit([], [], [])'); + pause(0.2); + secondBar = evalc('unit([], [], [])'); + + testCase.verifyTrue(contains(firstBar, 'Processing')); + testCase.verifyTrue(contains(firstBar, '1it')); + + testCase.verifyTrue(contains(secondBar, '2it')); + unit.release(); + end + + + function canPrintBarWithTotal(testCase) + unit = testCase.getUnit(2); + + firstBar = evalc('unit([], [], [])'); + testCase.verifyTrue(contains(firstBar, 'Processing')); + testCase.verifyTrue(contains(firstBar, '050%')); + testCase.verifyTrue(contains(firstBar, '1/2')); + + secondBar = evalc('unit([], [], [])'); + testCase.verifyTrue(contains(secondBar, '100%')); + testCase.verifyTrue(contains(secondBar, '2/2')); + + unit.release(); + end + + + function canPrintLongTitle(testCase) + unit = testCase.getUnit(2); + unit.Title = 'This is a long title string'; + + firstBar = evalc('unit([], [], [])'); + secondBar = evalc('unit([], [], [])'); + unit.release(); + + testCase.verifyNotEqual(firstBar, secondBar); + end end From fbf77075f8195909f6111f36e14e525fc328950b Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 10:36:49 +0100 Subject: [PATCH 12/30] Move images --- README.md | 8 ++++---- example1.gif => images/example1.gif | Bin example2.gif => images/example2.gif | Bin 3 files changed, 4 insertions(+), 4 deletions(-) rename example1.gif => images/example1.gif (100%) rename example2.gif => images/example2.gif (100%) diff --git a/README.md b/README.md index 12f7b6a..a511237 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ - [The Bar Gets Longer With Each Iteration](#the-bar-gets-longer-with-each-iteration) - [Strange Symbols in the Progress Bar](#strange-symbols-in-the-progress-bar) - [Remaining Timer Objects in MATLAB's Background](#remaining-timer-objects-in-matlabs-background) - - [Issues concerning parallel processing](#issues-concerning-parallel-processing) + - [Issues Concerning Parallel Processing](#issues-concerning-parallel-processing) - [License](#license) This project hosts the source code to the [original MATLAB FileExchange project](https://de.mathworks.com/matlabcentral/fileexchange/57895-matlabprogressbar) and is place of active development. @@ -26,7 +26,7 @@ A design target was to mimic the best features of the progress bar [tqdm](https: Several projects exist on MATLAB's [File Exchange](https://www.mathworks.com/matlabcentral/fileexchange/?term=progress+bar) but none incorporates the feature set shown below. That's why I decided to start this project. -![Example 1](example1.gif) +![Example 1](images/example1.gif) **Supported features include (or are planned)**: - [ ] have a template functionality like in [minibar](https://github.com/canassa/minibar). Maybe use `regexprep()`? @@ -116,7 +116,7 @@ for iIteration = progress(1:numIterations) end ``` -![Example 2](example2.gif) +![Example 2](images/example2.gif) ### Parallel Toolbox Support @@ -171,7 +171,7 @@ Sometimes, if the user cancels a loop in which a progress bar was used, the dest ProgressBar.deleteAllTimers(); ``` -### Issues concerning parallel processing +### Issues Concerning Parallel Processing The work-flow when using the progress bar in a parallel setup is to instantiate the object with the `IsParallel` switch set to `true` and using the `updateParallel()` function to update the progress state instead of the `step()` method of the object. If this results in strange behavior check the following list. Generally, it is advisable to **first be sure that the executed code or functions in the parallel setup run without errors or warnings.** If not the execution may prevent the class destructor to properly clean up all files and timer objects. diff --git a/example1.gif b/images/example1.gif similarity index 100% rename from example1.gif rename to images/example1.gif diff --git a/example2.gif b/images/example2.gif similarity index 100% rename from example2.gif rename to images/example2.gif From 1596fa460ad4bfc50cb70ae71e71b4d67ab5439c Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 11:25:47 +0100 Subject: [PATCH 13/30] Test parallel setup --- ProgressBar_test.m | 53 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/ProgressBar_test.m b/ProgressBar_test.m index 1a2999e..16154c4 100644 --- a/ProgressBar_test.m +++ b/ProgressBar_test.m @@ -42,7 +42,7 @@ function deleteRogueTimers(~) - methods (Test) + methods (Test, TestTags = {'Normal'}) function testTimerDeletion(testCase) unit = testCase.getUnit(); @@ -93,7 +93,7 @@ function checkBarLengthInput(testCase) unit = testCase.getUnit(); testCase.verifyEqual(unit.checkInputOfTotal([]), true); - testCase.verifyEqual(unit.checkInputOfTotal(10), false); + testCase.verifyEqual(unit.checkInputOfTotal(10), true); testCase.verifyError(@() unit.checkInputOfTotal('char'), 'MATLAB:invalidType'); testCase.verifyError(@() unit.checkInputOfTotal(-1), 'MATLAB:expectedPositive'); @@ -184,18 +184,59 @@ function canPrintLongTitle(testCase) testCase.verifyNotEqual(firstBar, secondBar); end + + + function canRunWithoutUpdateTimer(testCase) + unit = testCase.getUnit(2, 'UpdateRate', inf); + + firstBar = evalc('unit([], [], [])'); + secondBar = evalc('unit([], [], [])'); + unit.release(); + + testCase.verifyTrue(contains(firstBar, '1/2it')); + testCase.verifyTrue(contains(secondBar, '2/2it')); + end + end + + + methods (Test, TestTags = {'Parallel'}) + function canRunInParallel(testCase) + numIterations = 100; + + unit = testCase.getUnit(numIterations, ... + 'IsParallel', true, ... + 'WorkerDirectory', pwd() ... + ); + + init = evalc('unit.setup([], [], []);'); + parfor iIteration = 1:numIterations + pause(0.1); + updateParallel([], pwd()); + end + unit.release(); + + testCase.verifyTrue(contains(init, '000%')); + testCase.verifyTrue(contains(init, sprintf('0/%dit', numIterations))); + testCase.verifyEmpty(timerfindall('Tag', 'ProgressBar')); + + pause(0.5); + files = unit.findWorkerFiles(pwd()); + testCase.verifyEmpty(files); + end end methods - function [unit] = getUnit(testCase, len) - if nargin < 2 || isempty(len) - len = []; + function [unit] = getUnit(testCase, total, varargin) + % Factory to get a bar object with desired 'total' + + if nargin < 2 + total = []; end unitHandle = str2func(testCase.UnitName); - unit = unitHandle(len); + unit = unitHandle(total, varargin{:}); end end From 9f52eec1795541416ae803a4a52fdeb609c7e1f5 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 11:57:31 +0100 Subject: [PATCH 14/30] Update README --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index a511237..3d8a2b3 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ - [Dependencies](#dependencies) - [Installation](#installation) - [Usage](#usage) - - [Extended Use with all Features](#extended-use-with-all-features) - - [Proposed Use for Simple Loops](#proposed-use-for-simple-loops) + - [Proposed Usage for Simple Loops](#proposed-usage-for-simple-loops) + - [Extended Usage with all Features](#extended-usage-with-all-features) - [Parallel Toolbox Support](#parallel-toolbox-support) - [Known Issues](#known-issues) - [Flickering Bar or Flooding of the Command Window](#flickering-bar-or-flooding-of-the-command-window) @@ -69,7 +69,22 @@ Put the files `ProgressBar.m`, `progress.m` and `updateParallel.m` into your MAT Detailed information and examples about all features of `ProgressBar` are stated in the demo scripts in the `./demos/` directory. -### Extended Use with all Features +### Proposed Usage for Simple Loops +The simplest use in `for`-loops is to use the `progress()` function. It wraps the main `ProgressBar` class and is intended to only support the usual progress bar. Be aware that functionalities like `printMessage()`, printing success information or a step size different to 1 are not supported with `progress.m`. Also, this only works for **non-parallel** loops. + +See the example below: +```matlab +numIterations = 10e3; + +% create the loop using the progress() class +for iIteration = progress(1:numIterations) + % do some processing +end +``` + +![Example 2](images/example2.gif) + +### Extended Usage with all Features The basic work flow is to instantiate a `ProgressBar` object and use either the `step()` method to update the progress state (MATLAB <= R2015b) or use the instantiated object directly as seen below. Refer to the method's help for information about input parameters. The shown call is the *default* call and sufficient. If you want to pass information about the step size, the iteration's success or if a new bar should be printed immediately (e.g. when iterations take long time) you can pass these information instead of empty matrices. All settings are done using *name-value* pairs in the constructor. It is **strongly encouraged** to call the object's `release()` method after the loop is finished to clean up the internal state and avoid possibly unrobust behavior of following progress bars. @@ -103,21 +118,6 @@ end progBar.release(); ``` -### Proposed Use for Simple Loops -A neat way to completely get rid of the conventional updating process is to use the `progress.m` wrapper class. It implements the `subsref()` method and, thus, acts similar to an iterator in Python. A progress bar will be printed without the further need to call `step()`. Be aware that functionalities like `printMessage()`, printing success information or a step size different to 1 are not supported with `progress.m`. - -See the example below: -```matlab -numIterations = 10e3; - -% create the loop using the progress() class -for iIteration = progress(1:numIterations) - % do some processing -end -``` - -![Example 2](images/example2.gif) - ### Parallel Toolbox Support If you use MATLAB's Parallel Computing Toolbox, refer to the following example or the demo file `k_parallelSetup.m`. Tested parallel functionalities are `parfor` and `parfeval()` for asynchronous processing. From df1f9a6db0476b45501e20253ea5b9276327cbaa Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 11:57:49 +0100 Subject: [PATCH 15/30] Update CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea3ac42..23e26b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ 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.1.0] - 2019-11-26 +## [Unreleased] +### 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` +- Proper unit testing as a test-case class + ### Changed +- Demos should now be a bit prettier + + +## [3.1.1] - 2019-11-26 +### Fixed - Updated CHANGELOG ### Added From ca20f63121ce194a55a5cb1960907fa5dd14b921 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 12:03:09 +0100 Subject: [PATCH 16/30] Include unittest statement --- ProgressBar_test.m | 2 +- README.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ProgressBar_test.m b/ProgressBar_test.m index 16154c4..fababb7 100644 --- a/ProgressBar_test.m +++ b/ProgressBar_test.m @@ -42,7 +42,7 @@ function deleteRogueTimers(~) - methods (Test, TestTags = {'Normal'}) + methods (Test, TestTags = {'NonParallel'}) function testTimerDeletion(testCase) unit = testCase.getUnit(); diff --git a/README.md b/README.md index 3d8a2b3..2e996e8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - [Strange Symbols in the Progress Bar](#strange-symbols-in-the-progress-bar) - [Remaining Timer Objects in MATLAB's Background](#remaining-timer-objects-in-matlabs-background) - [Issues Concerning Parallel Processing](#issues-concerning-parallel-processing) + - [Unit Tests](#unit-tests) - [License](#license) This project hosts the source code to the [original MATLAB FileExchange project](https://de.mathworks.com/matlabcentral/fileexchange/57895-matlabprogressbar) and is place of active development. @@ -185,6 +186,12 @@ The work-flow when using the progress bar in a parallel setup is to instantiate **TL/DR**: `clear all` and `delete(timerfindall('Tag', 'ProgressBar'))` are your friend! Be sure that no files following the pattern `progbarworker_*` remain in the directory returned by `tempdir()`. +## Unit Tests +You can run all available tests in the project directory by executing simply `runtests` in MATLAB. However, if you want to omit the parallel tests (e.g. you don't have the Parallel Toolbox installed), just execute + +```matlab +runtests Tag NonParallel +``` ## License From ce181d91efa0b8d4c724d1fcacc7c988103a1e95 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 12:18:04 +0100 Subject: [PATCH 17/30] Move test to test folder --- ProgressBar_test.m => tests/ProgressBar_test.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) rename ProgressBar_test.m => tests/ProgressBar_test.m (96%) diff --git a/ProgressBar_test.m b/tests/ProgressBar_test.m similarity index 96% rename from ProgressBar_test.m rename to tests/ProgressBar_test.m index fababb7..1198f17 100644 --- a/ProgressBar_test.m +++ b/tests/ProgressBar_test.m @@ -14,7 +14,6 @@ properties UnitName = "ProgressBar"; - Seed; end @@ -26,6 +25,15 @@ function setClassRng(testCase) rng(testCase.DEFAULT_SEED); end + + function addPath(testCase) + defaultPath = matlabpath(); + + myLocation = fileparts(mfilename('fullpath')); + addpath(fullfile(myLocation, '..')); + + testCase.addTeardown(@() matlabpath(defaultPath)); + end end methods (TestMethodSetup) From 7c053b35d0472feef7b48e87358d9f5dd50c680f Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 12:30:41 +0100 Subject: [PATCH 18/30] Test for progress() --- progress.m | 4 ++- tests/progress_test.m | 73 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 tests/progress_test.m diff --git a/progress.m b/progress.m index 218992d..faa4254 100644 --- a/progress.m +++ b/progress.m @@ -61,7 +61,9 @@ % Class Destructor function delete(obj) % call the destructor of the ProgressBar() object - obj.ProgressBar.release(); + if ~isempty(obj.ProgressBar) + obj.ProgressBar.release(); + end end function [varargout] = subsref(obj, S) diff --git a/tests/progress_test.m b/tests/progress_test.m new file mode 100644 index 0000000..17c6149 --- /dev/null +++ b/tests/progress_test.m @@ -0,0 +1,73 @@ +classdef progress_test < matlab.unittest.TestCase + %PROGRESS_TEST Unit test for progress.m + % ------------------------------------------------------------------------- + % Run it by calling 'runtests()' + % or specifically 'runtests('progress_test')' + % + % Author : J.-A. Adrian (JA) + % + + + properties (Constant) + DEFAULT_SEED = 123; + end + + properties (Access = public) + Seed; + end + + + + methods (TestClassSetup) + function setClassRng(testCase) + testCase.Seed = rng(); + testCase.addTeardown(@rng, testCase.Seed); + + rng(testCase.DEFAULT_SEED); + end + + function addPath(testCase) + defaultPath = matlabpath(); + + myLocation = fileparts(mfilename('fullpath')); + addpath(fullfile(myLocation, '..')); + + testCase.addTeardown(@() matlabpath(defaultPath)); + end + end + + methods (TestMethodSetup) + function setMethodRng(testCase) + rng(testCase.DEFAULT_SEED); + end + end + + methods (TestMethodTeardown) + function deleteRogueTimers(~) + delete(timerfindall('Tag', ProgressBar.TIMER_TAG_NAME)); + end + end + + + + methods (Test) + function earlyExit(testCase) + obj = progress(); + + testCase.verifyClass(obj, 'progress'); + testCase.verifyEmpty(properties(obj)); + + methodsAreImplemented = contains(methods(obj), {'progress', 'size', 'subsref'}); + testCase.verifyEqual(sum(methodsAreImplemented), 3); + end + + + function simpleCall(testCase) + str = evalc('for k = progress(1); end;'); + + testCase.verifyTrue(contains(str, '100%')); + testCase.verifyTrue(contains(str, '1/1it')); + end + end + +end From c21f088852b149c42e3970d653eb40fd16d6dd1a Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 12:36:25 +0100 Subject: [PATCH 19/30] Add missing test tag --- README.md | 4 ++-- tests/progress_test.m | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2e996e8..422cd34 100644 --- a/README.md +++ b/README.md @@ -187,10 +187,10 @@ The work-flow when using the progress bar in a parallel setup is to instantiate `clear all` and `delete(timerfindall('Tag', 'ProgressBar'))` are your friend! Be sure that no files following the pattern `progbarworker_*` remain in the directory returned by `tempdir()`. ## Unit Tests -You can run all available tests in the project directory by executing simply `runtests` in MATLAB. However, if you want to omit the parallel tests (e.g. you don't have the Parallel Toolbox installed), just execute +You can run all available tests in the project directory by navigating into the `tests` folder and executing `runtests` in MATLAB. However, if you want to omit the parallel tests (e.g. you don't have the Parallel Toolbox installed), just execute ```matlab -runtests Tag NonParallel +runtests tests Tag NonParallel ``` ## License diff --git a/tests/progress_test.m b/tests/progress_test.m index 17c6149..1518efa 100644 --- a/tests/progress_test.m +++ b/tests/progress_test.m @@ -50,7 +50,7 @@ function deleteRogueTimers(~) - methods (Test) + methods (Test, TestTags = {'NonParallel'}) function earlyExit(testCase) obj = progress(); From a94a284889ac3e6d30de2ddbad0a19d4dfc68c4b Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 12:42:09 +0100 Subject: [PATCH 20/30] Fix README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 422cd34..5045d11 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ The work-flow when using the progress bar in a parallel setup is to instantiate You can run all available tests in the project directory by navigating into the `tests` folder and executing `runtests` in MATLAB. However, if you want to omit the parallel tests (e.g. you don't have the Parallel Toolbox installed), just execute ```matlab -runtests tests Tag NonParallel +runtests Tag NonParallel ``` ## License From a677bc637ce3ee03589a8482803285edb02c8357 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Sat, 7 Nov 2020 22:32:15 +0100 Subject: [PATCH 21/30] Set checkmark in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5045d11..1c27c4d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Several projects exist on MATLAB's [File Exchange](https://www.mathworks.com/mat **Supported features include (or are planned)**: - [ ] have a template functionality like in [minibar](https://github.com/canassa/minibar). Maybe use `regexprep()`? -- [ ] proper unit testing +- [x] proper unit testing - [x] display the bar name as a ticker. That way, a fixed bar width could be used - [x] inherit from MATLAB System Object to gain benefits from the setup method - [ ] use [this new functionality](https://de.mathworks.com/help/distcomp/send.html) for the parallel implementation. Introduced in R2017a. From 45beb3f42b0072747e99eb655f6c89380875af0b Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Thu, 12 Nov 2020 08:29:05 +0100 Subject: [PATCH 22/30] Hidden constants, internal property renaming --- ProgressBar.m | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ProgressBar.m b/ProgressBar.m index 157840e..694fc44 100644 --- a/ProgressBar.m +++ b/ProgressBar.m @@ -99,22 +99,22 @@ TimerObject; MaxBarWidth = 90; - CurrentTitleState = ''; - CurrentFont; end - properties (Constant, Access = private) + properties (Constant, Hidden) % The number of sub blocks in one main block of width of a character. % HTML 'left blocks' go in eigths -> 8 sub blocks in one main block - NumSubBlocks = 8; + NUM_SUB_BLOCKS = 8; % The number of characters the title string should shift each cycle - NumCharactersShift = 3; + NUM_CHARACTERS_SHIFT = 3; % The maximum length of the title string without banner cycling - MaxTitleLength = 20; + MAX_TITLE_LENGTH = 20; + + OVERRIDE_FONT_NAME = 'Courier New'; end properties (Access = private, Dependent) @@ -246,7 +246,7 @@ 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 = 'Courier New'; + s.matlab.fonts.codefont.Name.TemporaryValue = ob.OVERRIDE_FONT_NAME; end % add a new timer object with the standard tag name and hide it @@ -270,7 +270,7 @@ end obj.CurrentTitleState = obj.Title; - if length(obj.Title) > obj.MaxTitleLength + if length(obj.Title) > obj.MAX_TITLE_LENGTH obj.CurrentTitleState = [obj.CurrentTitleState, ' -- ']; end @@ -419,7 +419,7 @@ % Compute the progress percentage of a single main and a single sub % block obj.FractionMainBlock = 1 / length(obj.Bar); - obj.FractionSubBlock = obj.FractionMainBlock / obj.NumSubBlocks; + obj.FractionSubBlock = obj.FractionMainBlock / obj.NUM_SUB_BLOCKS; end @@ -432,7 +432,7 @@ % insert worst case inputs to get (almost) maximum length of bar preBar = sprintf(... preBarFormat, ... - blanks(min(length(obj.CurrentTitleState), obj.MaxTitleLength)), ... + blanks(min(length(obj.CurrentTitleState), obj.MAX_TITLE_LENGTH)), ... 100 ... ); postBar = sprintf(... @@ -572,7 +572,7 @@ argList = { obj.CurrentTitleState(... - 1:min(length(obj.Title), obj.MaxTitleLength) ... + 1:min(length(obj.Title), obj.MAX_TITLE_LENGTH) ... ), ... floor(obj.IterationCounter / obj.Total * 100), ... barString, ... @@ -596,7 +596,7 @@ argList = { obj.CurrentTitleState(... - 1:min(length(obj.Title), obj.MaxTitleLength) ... + 1:min(length(obj.Title), obj.MAX_TITLE_LENGTH) ... ), ..., ... scaledIteration, ... etHoursMinsSecs(1), ... @@ -625,7 +625,7 @@ % index of the current sub block continuousBlockIndex = ceil(currProgress / obj.FractionSubBlock); - thisSubBlock = mod(continuousBlockIndex - 1, obj.NumSubBlocks) + 1; + thisSubBlock = mod(continuousBlockIndex - 1, obj.NUM_SUB_BLOCKS) + 1; % fix for non-full last blocks when steps are large: make them full obj.Bar(1:max(thisMainBlock-1, 0)) = ... @@ -761,8 +761,8 @@ function [] = updateCurrentTitle(obj) strTitle = obj.CurrentTitleState; - if length(strTitle) > obj.MaxTitleLength - strTitle = circshift(strTitle, -obj.NumCharactersShift); + if length(strTitle) > obj.MAX_TITLE_LENGTH + strTitle = circshift(strTitle, -obj.NUM_CHARACTERS_SHIFT); obj.CurrentTitleState = strTitle; end From f2e7fddbc2752af65c87b6f0af07e5924f232435 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 09:33:37 +0100 Subject: [PATCH 23/30] Fix indenting --- progress.m | 154 ++++++++++++++++++++++++++--------------------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/progress.m b/progress.m index faa4254..c77ac7d 100644 --- a/progress.m +++ b/progress.m @@ -1,85 +1,85 @@ classdef progress < handle -%PROGRESS Wrapper class to provide an iterator object for loop creation -% ------------------------------------------------------------------------- -% This class provides the possibility to create an iterator object in -% MATLAB to make the handling of ProgressBar() even easier. The following -% example shows the usage. Although no ProgressBar() is called by the user, -% a progress bar is shown. The input arguments are the same as for -% ProgressBar(), so please refer to the documentation of ProgressBar(). -% -% Note that this implementation is slower than the conventional -% ProgressBar() class since the subsref() method is called with -% non-optimized values in every iteration. -% -% ========================================================================= -% Example: -% -% for k = progress(1:100) -% % do some processing -% end -% -% Or with additional name-value pairs: -% -% for k = progress(1:100, ... -% 'Title', 'Computing...' ... -% ) -% -% % do some processing -% end -% -% ========================================================================= -% -% progress Properties: -% none -% -% progress Methods: -% progress - class constructor -% -% -% Author : J.-A. Adrian (JA) -% - - -properties (Access = private) - IterationList; - ProgressBar; -end - -methods - % Class Constructor - function obj = progress(in, varargin) - if ~nargin - return; - end - - obj.IterationList = in; - - % pass all varargins to ProgressBar() - obj.ProgressBar = ProgressBar(length(in), varargin{:}); - end - - % Class Destructor - function delete(obj) - % call the destructor of the ProgressBar() object - if ~isempty(obj.ProgressBar) - obj.ProgressBar.release(); - end - end + %PROGRESS Wrapper class to provide an iterator object for loop creation + % ------------------------------------------------------------------------- + % This class provides the possibility to create an iterator object in + % MATLAB to make the handling of ProgressBar() even easier. The following + % example shows the usage. Although no ProgressBar() is called by the user, + % a progress bar is shown. The input arguments are the same as for + % ProgressBar(), so please refer to the documentation of ProgressBar(). + % + % Note that this implementation is slower than the conventional + % ProgressBar() class since the subsref() method is called with + % non-optimized values in every iteration. + % + % ========================================================================= + % Example: + % + % for k = progress(1:100) + % % do some processing + % end + % + % Or with additional name-value pairs: + % + % for k = progress(1:100, ... + % 'Title', 'Computing...' ... + % ) + % + % % do some processing + % end + % + % ========================================================================= + % + % progress Properties: + % none + % + % progress Methods: + % progress - class constructor + % + % + % Author : J.-A. Adrian (JA) + % - function [varargout] = subsref(obj, S) - % This method implements the subsref method and only calls the update() - % method of ProgressBar. The actual input 'S' is passed to the default - % subsref method of the class of obj.IterationList. - obj.ProgressBar.step([], [], []); - varargout = {subsref(obj.IterationList, S)}; + properties (Access = private) + IterationList; + ProgressBar; end - function [m, n] = size(obj) - % This method implements the size() function for the progress() class. - - [m, n] = size(obj.IterationList); + methods + % Class Constructor + function obj = progress(in, varargin) + if ~nargin + return; + end + + obj.IterationList = in; + + % pass all varargins to ProgressBar() + obj.ProgressBar = ProgressBar(length(in), varargin{:}); + end + + % Class Destructor + function delete(obj) + % call the destructor of the ProgressBar() object + if ~isempty(obj.ProgressBar) + obj.ProgressBar.release(); + end + end + + function [varargout] = subsref(obj, S) + % This method implements the subsref method and only calls the update() + % method of ProgressBar. The actual input 'S' is passed to the default + % subsref method of the class of obj.IterationList. + + obj.ProgressBar.step([], [], []); + varargout = {subsref(obj.IterationList, S)}; + end + + function [m, n] = size(obj) + % This method implements the size() function for the progress() class. + + [m, n] = size(obj.IterationList); + end end end -end From 6576f598d3b0852fea713773c4bfab3b71b71966 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 09:37:53 +0100 Subject: [PATCH 24/30] Show simple example in README first --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c27c4d..ce37e1e 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A design target was to mimic the best features of the progress bar [tqdm](https: Several projects exist on MATLAB's [File Exchange](https://www.mathworks.com/matlabcentral/fileexchange/?term=progress+bar) but none incorporates the feature set shown below. That's why I decided to start this project. -![Example 1](images/example1.gif) +![Easy progress bar example](images/example2.gif) **Supported features include (or are planned)**: - [ ] have a template functionality like in [minibar](https://github.com/canassa/minibar). Maybe use `regexprep()`? @@ -119,6 +119,8 @@ end progBar.release(); ``` +![Extended usage with the object method calls](images/example1.gif) + ### Parallel Toolbox Support If you use MATLAB's Parallel Computing Toolbox, refer to the following example or the demo file `k_parallelSetup.m`. Tested parallel functionalities are `parfor` and `parfeval()` for asynchronous processing. From 8d5c52e8122035a737d65295a1bb717f29bf2506 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 09:45:42 +0100 Subject: [PATCH 25/30] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e26b5..2c90fec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Demos should now be a bit prettier +### Fixed +- Progress bar in non-finite and iteration mode (not having a konwn total number of iterations) will now show correct unit string `it` instead of toggling wrongly between `it` and `s` ## [3.1.1] - 2019-11-26 ### Fixed From eb38bfe695228c2605e1c735001fd98697880797 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 21:25:14 +0100 Subject: [PATCH 26/30] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce37e1e..1889dad 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ Several projects exist on MATLAB's [File Exchange](https://www.mathworks.com/mat ![Easy progress bar example](images/example2.gif) **Supported features include (or are planned)**: -- [ ] have a template functionality like in [minibar](https://github.com/canassa/minibar). Maybe use `regexprep()`? - [x] proper unit testing - [x] display the bar name as a ticker. That way, a fixed bar width could be used - [x] inherit from MATLAB System Object to gain benefits from the setup method @@ -49,6 +48,7 @@ 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()`? **Note**: @@ -58,7 +58,7 @@ Be sure to have a look at the [Known Issues](#known-issues) section for current No dependencies to toolboxes. -The code has been tested with MATLAB R2016a and R2016b on Windows 7 and Xubuntu 16.04.2 LTS. +The code has been tested with MATLAB R2016a, R2016b and R2020b on Windows 10, Xubuntu 16.04.2 LTS and Linux Mint 20. ## Installation From 8fd4e4229a4ac5d0b649439dd0c6870b71df45d6 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 21:25:26 +0100 Subject: [PATCH 27/30] Update the demo files --- demos/a_ProgressBarWithoutTotal_demo.m | 43 --------- demos/b_simpleProgressBar_demo.m | 94 ------------------- demos/basicUseCases.m | 92 ++++++++++++++++++ demos/c_SpecificUpdateRate_demo.m | 56 ----------- ...ProcessedBytes.m => countProcessedBytes.m} | 30 +++--- demos/i_progressLoop.m | 37 -------- ...ogressBars_demo.m => nestedProgressBars.m} | 53 +++++------ ...eProgressBar.m => nonUnicodeProgressBar.m} | 18 ++-- demos/{k_parallelSetup.m => parallelSetup.m} | 31 +++--- ...ssSuccessInfo_demo.m => passSuccessInfo.m} | 20 ++-- ...oDuringRun_demo.m => printInfoDuringRun.m} | 21 ++--- demos/{h_titleBanner.m => titleBanner.m} | 21 ++--- 12 files changed, 170 insertions(+), 346 deletions(-) delete mode 100644 demos/a_ProgressBarWithoutTotal_demo.m delete mode 100644 demos/b_simpleProgressBar_demo.m create mode 100644 demos/basicUseCases.m delete mode 100644 demos/c_SpecificUpdateRate_demo.m rename demos/{e_CountProcessedBytes.m => countProcessedBytes.m} (63%) delete mode 100644 demos/i_progressLoop.m rename demos/{d_NestedProgressBars_demo.m => nestedProgressBars.m} (57%) rename demos/{j_NonUnicodeProgressBar.m => nonUnicodeProgressBar.m} (60%) rename demos/{k_parallelSetup.m => parallelSetup.m} (70%) rename demos/{g_PassSuccessInfo_demo.m => passSuccessInfo.m} (60%) rename demos/{f_PrintInfoDuringRun_demo.m => printInfoDuringRun.m} (50%) rename demos/{h_titleBanner.m => titleBanner.m} (55%) diff --git a/demos/a_ProgressBarWithoutTotal_demo.m b/demos/a_ProgressBarWithoutTotal_demo.m deleted file mode 100644 index 7704153..0000000 --- a/demos/a_ProgressBarWithoutTotal_demo.m +++ /dev/null @@ -1,43 +0,0 @@ -% Demo for a simple progress bar without known total number of iterations -% -% Author: J.-A. Adrian (JA) -% Date : 21-Jun-2016 17:13:27 -% - - -addpath('..'); - -numIterations = 50; - - - -%% Simple setup WITHOUT known number of iterations - -obj = ProgressBar(); - -for iIteration = 1:numIterations - pause(0.1); - - obj.step(1, [], []); -end -obj.release(); - - - - -%% Simple setup WITHOUT known number of iterations and with custom title - -obj = ProgressBar([], 'Title', 'Test'); - -for iIteration = 1:numIterations - pause(0.1); - - obj.step(1, [], []); -end -obj.release(); - - - - - -% End of file: a_ProgressBarWithoutTotal_demo.m diff --git a/demos/b_simpleProgressBar_demo.m b/demos/b_simpleProgressBar_demo.m deleted file mode 100644 index 962fc6c..0000000 --- a/demos/b_simpleProgressBar_demo.m +++ /dev/null @@ -1,94 +0,0 @@ -% Demo of some standard applications with known total number of iterations -% -% Author: J.-A. Adrian (JA) -% Date : 21-Jun-2016 17:12:41 -% - - -addpath('..'); - -numIterations = 50; - - -%% Simple setup WITH known number of iterations - -obj = ProgressBar(numIterations); -for iIteration = 1:numIterations - pause(0.1); - - obj.step([], [], []); -end -obj.release(); - - - - -%% Simple setup WITH known number of iterations and title - -obj = ProgressBar(numIterations, ... - 'Title', 'Small' ... - ); - -for iIteration = 1:numIterations - pause(0.1); - - obj.step([], [], []); -end -obj.release(); - - - - -%% Now with a different step size - -obj = ProgressBar(numIterations, ... - 'Title', 'Different Step Size' ... - ); - -stepSize = 2; - -for iIteration = 1:stepSize:numIterations - pause(0.1); - - obj.step(stepSize, [], []); -end -obj.release(); - - - - -%% Simulate an iteration which takes longer so the timed printing stops - -pauses = [0.1*ones(numIterations/2-1,1); 2; 0.1*ones(numIterations/2,1)]; - -obj = ProgressBar(numIterations, ... - 'Title', 'Waiting' ... - ); - -for iIteration = 1:numIterations - pause(pauses(iIteration)); - - obj.step([], [], []); -end -obj.release(); - -%% Simulate a progress with it/sec < 1 - -numIterations = 10; -obj = ProgressBar(numIterations, ... - 'Title', 'Slow Progress, Long Title' ... - ); - -obj.setup([], [], []); -for iIteration = 1:numIterations - pause(1.5); - - obj.step([], [], []); -end -obj.release(); - - - - - -% End of file: b_simpleProgressBar_demo.m diff --git a/demos/basicUseCases.m b/demos/basicUseCases.m new file mode 100644 index 0000000..218e0b6 --- /dev/null +++ b/demos/basicUseCases.m @@ -0,0 +1,92 @@ +% This script shows all currently implemented features one by one. +% +% Author: J.-A. Adrian (JA) +% + +clear; +close all; + +addpath('..'); + +numIterations = 25; + + +%% Easy Setup WITH Known Number of Iterations +% This should be your every-day solution if you don't care for additional feature and just want to +% have a progress bar for your for-loop +for iIteration = progress(1:numIterations) + pause(0.3); +end + +% You can do the same thing but alter any bar's property (learn about those later on). For example, +% customize the shown bar title in the front. +for iIteration = progress(1:numIterations, 'Title', 'New Title') + pause(0.3); +end + + +%% Setup WITHOUT Known Number of Iterations +% If you just want to keep track of iterations, use the main class and don't pass a total number of +% iterations. Here, we instanciate a progress bar object and use it for iteration updates. +b = ProgressBar(); + +counter = 0; +while counter < numIterations + counter = counter + 1; + pause(0.3); + + b(1, [], []); +end +b.release(); + + +% You can do the same thing but alter any bar's property (learn about those later on). For example, +% customize the shown bar title in the front. +b = ProgressBar([], 'Title', 'Test'); + +counter = 0; +while counter < numIterations + counter = counter + 1; + pause(0.3); + + b(1, [], []); +end +b.release(); + + +%% Custom Print Update Rate +% The default update rate is 5 Hz. We can change it for smoother looks. +numIterations = 5e5; + +% change the update rate to, e.g., 10 Hz +b = ProgressBar(numIterations, ... + 'UpdateRate', 10 ... + ); + +for iIteration = 1:numIterations + b(1, [], []); +end +b.release(); + + +% Set the upate rate to infinity to print at every call of the progress bar +updateRateHz = inf; +numIterations = 100; + +% pass the number of iterations and the update cycle in Hz +b = ProgressBar(numIterations, ... + 'UpdateRate', updateRateHz ... + ); + +for iIteration = 1:numIterations + b(1, [], []); + + pause(0.1); +end +b.release(); + + + + + + diff --git a/demos/c_SpecificUpdateRate_demo.m b/demos/c_SpecificUpdateRate_demo.m deleted file mode 100644 index c837888..0000000 --- a/demos/c_SpecificUpdateRate_demo.m +++ /dev/null @@ -1,56 +0,0 @@ -% Demo how to manipulate the update rate -% -% Author: J.-A. Adrian (JA) -% Date : 21-Jun-2016 17:14:54 -% - - -addpath('..'); - -numIterations = 1e4; - - -%% Desired update rate should be 10 Hz (the default is 5 Hz) - -updateRateHz = 10; - -% pass the number of iterations and the update cycle in Hz -obj = ProgressBar(numIterations, ... - 'UpdateRate', updateRateHz ... - ); - -for iIteration = 1:numIterations - obj.step(1, [], []); -end -obj.release(); - - - - -%% No desired update rate -% (incorporate a pause to prevent faster updates than MATLAB can print) - -numIterations = 100; - -updateRateHz = inf; - -% pass the number of iterations and the update cycle in Hz -obj = ProgressBar(numIterations, ... - 'UpdateRate', updateRateHz ... - ); - -for iIteration = 1:numIterations - obj.step(1, [], []); - - pause(0.1); -end -obj.release(); - - - - - - - - -% End of file: c_SpecificUpdateRate_demo.m diff --git a/demos/e_CountProcessedBytes.m b/demos/countProcessedBytes.m similarity index 63% rename from demos/e_CountProcessedBytes.m rename to demos/countProcessedBytes.m index 557ae1d..be68e5c 100644 --- a/demos/e_CountProcessedBytes.m +++ b/demos/countProcessedBytes.m @@ -1,10 +1,10 @@ -% Demo of another counting unit. At this point, only 'Bytes' is supported -% as alternative. +% Demo of another counting unit. At this point, only 'Bytes' is supported as alternative. % % Author: J.-A. Adrian (JA) -% Date : 22-Jun-2016 12:32:50 % +clear; +close all; addpath('..'); @@ -15,9 +15,8 @@ numTotalBytes = sum(cellfun(@(x) size(x, 1), dummyFile)); -%% Work with size of processed bytes WITHOUT knowledge of total bytes - -obj = ProgressBar([], ... +%% Work with Size of Processed Bytes WITHOUT Knowledge of Total Bytes +b = ProgressBar([], ... 'Unit', 'Bytes', ... 'Title', 'Test Bytes 1' ... ); @@ -26,33 +25,26 @@ buffer = dummyFile{iFile}; pause(filePause(iFile)); - obj.step(length(buffer), [], []); + b(length(buffer), [], []); end -obj.release(); - +b.release(); -%% Work with size of processed bytes WITH knowledge of total bytes - -obj = ProgressBar(numTotalBytes, ... +%% Work With Size of Processed Bytes WITH Knowledge of Total Bytes +b = ProgressBar(numTotalBytes, ... 'Unit', 'Bytes', ... 'Title', 'Test Bytes 2' ... ); for iFile = 1:length(dummyFile) buffer = dummyFile{iFile}; - pause(filePause(iFile)); - obj.step(length(buffer), [], []); + b.step(length(buffer), [], []); end -obj.release(); - - - +b.release(); -% End of file: e_CountProcessedBytes.m diff --git a/demos/i_progressLoop.m b/demos/i_progressLoop.m deleted file mode 100644 index 12a4c26..0000000 --- a/demos/i_progressLoop.m +++ /dev/null @@ -1,37 +0,0 @@ -% Demo of the progress() wrapper object. This can be used in for loops to -% get rid of the update() method. All supported constructor options will -% internally be passed to the ProgressBar() constructor. -% -% Author: J.-A. Adrian (JA) -% Date : 25-Jun-2016 11:25:15 -% - - -addpath('..'); - -numIterations = 1e5; - - -%% No title and specialties - -for iIteration = progress(1:numIterations) - % do nothing and print only -end - - - - -%% title and specific update rate -for iIteration = progress(1:numIterations, ... - 'Title', 'Iterator', ... - 'UpdateRate', 5) - - % do nothing and print only -end - - - - - - -% End of file: i_progressLoop.m diff --git a/demos/d_NestedProgressBars_demo.m b/demos/nestedProgressBars.m similarity index 57% rename from demos/d_NestedProgressBars_demo.m rename to demos/nestedProgressBars.m index 59b1ce5..524ad74 100644 --- a/demos/d_NestedProgressBars_demo.m +++ b/demos/nestedProgressBars.m @@ -1,7 +1,6 @@ % Demo of nested bars. At this point only one nested bar is supported % % Author: J.-A. Adrian (JA) -% Date : 21-Jun-2016 17:14:36 % @@ -11,71 +10,61 @@ numInnerIterations = 15; - -%% Nested Bars without inner update rate - -% be sure to set the update rate to inf to disable a timed printing of the -% bar! -obj1 = ProgressBar(numOuterIterations, ... +%% Nested Bars without Inner Update Rate +% be sure to set the update rate to inf to disable a timed printing of the bar! +b1 = ProgressBar(numOuterIterations, ... 'UpdateRate', inf, ... 'Title', 'Loop 1' ... ); % helper method to print a first progress bar before the inner loop starts. -% This prevents a blank line until the first obj1.step() is called. -obj1.setup([], [], []); +% This prevents a blank line until the first b1.step() is called. +b1.setup([], [], []); for iOuterIteration = 1:numOuterIterations - obj2 = ProgressBar(numInnerIterations, ... + b2 = ProgressBar(numInnerIterations, ... 'UpdateRate', inf, ... 'Title', 'Loop 2' ... ); - obj2.setup([], [], []); + b2.setup([], [], []); for jInnerIteration = 1:numInnerIterations - obj2.step(1, [], []); + b2(1, [], []); pause(0.1); end - obj2.release(); + b2.release(); - obj1.step(1, [], []); + b1(1, [], []); end -obj1.release(); - - +b1.release(); -%% Nested Bars WITH inner update rate +%% Nested Bars WITH Inner Update Rate numInnerIterations = 50e3; -% be sure to set the update rate to inf to disable a timed printing of the -% bar! -obj1 = ProgressBar(numOuterIterations, ... +% be sure to set the update rate to inf to disable a timed printing of the bar! +b1 = ProgressBar(numOuterIterations, ... 'UpdateRate', inf, ... 'Title', 'Loop 1' ... ); -obj1.setup([], [], []); +b1.setup([], [], []); for iOuterIteration = 1:numOuterIterations % this progress can have an update rate! - obj2 = ProgressBar(numInnerIterations, ... + b2 = ProgressBar(numInnerIterations, ... 'UpdateRate', 5, ... 'Title', 'Loop 2' ... ); - obj2.setup([], [], []); + b2.setup([], [], []); for jInnerIteration = 1:numInnerIterations - obj2.step(1, [], []); + b2.step(1, [], []); end - obj2.release(); + b2.release(); - obj1.step(1, [], []); + b1.step(1, [], []); end -obj1.release(); - - - +b1.release(); -% End of file: d_NestedProgressBars_demo.m diff --git a/demos/j_NonUnicodeProgressBar.m b/demos/nonUnicodeProgressBar.m similarity index 60% rename from demos/j_NonUnicodeProgressBar.m rename to demos/nonUnicodeProgressBar.m index 6ba6bcb..eed6b81 100644 --- a/demos/j_NonUnicodeProgressBar.m +++ b/demos/nonUnicodeProgressBar.m @@ -1,18 +1,18 @@ % Demo of using ASCII hashes instead of the fancy Unicode blocks. % % Author: J.-A. Adrian (JA) -% Date : 25-Jun-2016 14:26:43 % +clear; +close all; addpath('..'); numIterations = 100; -%% Don't use unicode characters - -obj = ProgressBar(numIterations, ... +%% Demo of Not Using Unicode Characters +b = ProgressBar(numIterations, ... 'UseUnicode', false, ... 'Title', 'ASCII' ... ); @@ -20,13 +20,7 @@ for iIteration = 1:numIterations pause(0.1); - obj.step([], [], []); + b([], [], []); end -obj.release(); - - - - - +b.release(); -% End of file: j_NonUnicodeProgressBar.m diff --git a/demos/k_parallelSetup.m b/demos/parallelSetup.m similarity index 70% rename from demos/k_parallelSetup.m rename to demos/parallelSetup.m index bddaecf..d4fe795 100644 --- a/demos/k_parallelSetup.m +++ b/demos/parallelSetup.m @@ -1,10 +1,11 @@ -% Demo of the parallel functionality using a parfor loop. This script may -% throw errors if you don't own the Parallel Processing Toolbox. +% Demo of the parallel functionality using a parfor loop. This script may throw errors if you don't +% own the Parallel Processing Toolbox. % % Author: J.-A. Adrian (JA) -% Date : 27-Jun-2016 22:04:18 % +clear; +close all; addpath('..'); @@ -15,48 +16,42 @@ end - -%% Without knowledge of total number of iterations - +%% Without Knowledge of Total Number of Iterations % Instantiate the object with the 'IsParallel' switch set to true and save % the aux. files in the default directory (tempdir) -obj = ProgressBar([], ... +b = ProgressBar([], ... 'IsParallel', true, ... 'Title', 'Parallel 1' ... ); % ALWAYS CALL THE SETUP() METHOD FIRST!!! -obj.setup([], [], []); +b.setup([], [], []); parfor iIteration = 1:numIterations pause(0.1); % USE THIS FUNCTION AND NOT THE STEP() METHOD OF THE OBJECT!!! updateParallel(); end -obj.release(); - - - +b.release(); -%% With knowledge of total number of iterations +%% With Knowledge of Total Number of Iterations % Instantiate the object with the 'Parallel' switch set to true and save % the aux. files in the current working directory (pwd) -obj = ProgressBar(numIterations, ... +b = ProgressBar(numIterations, ... 'IsParallel', true, ... - 'WorkerDirectory', pwd, ... + 'WorkerDirectory', pwd(), ... 'Title', 'Parallel 2' ... ); % ALWAYS CALL THE SETUP() METHOD FIRST!!! -obj.setup([], [], []); +b.setup([], [], []); parfor iIteration = 1:numIterations pause(0.1); % USE THIS FUNCTION AND NOT THE STEP() METHOD OF THE OBJECT!!! updateParallel([], pwd); end -obj.release(); +b.release(); -% End of file: k_parallelSetup.m diff --git a/demos/g_PassSuccessInfo_demo.m b/demos/passSuccessInfo.m similarity index 60% rename from demos/g_PassSuccessInfo_demo.m rename to demos/passSuccessInfo.m index 683e0c3..d6a1664 100644 --- a/demos/g_PassSuccessInfo_demo.m +++ b/demos/passSuccessInfo.m @@ -1,19 +1,19 @@ -% Demo of the success bool of the update() method. This can be used to -% print failure messages during the loop. +% Demo of the success bool of the update() method. This can be used to print failure messages during +% the loop. % % Author: J.-A. Adrian (JA) -% Date : 21-Jun-2016 17:48:54 % +clear; +close all; addpath('..'); numIterations = 1e2; -%% Pass success information of the current iteration - -obj = ProgressBar(numIterations, ... +%% Pass Success Information of the Current Iteration +b = ProgressBar(numIterations, ... 'Title', 'Test Success' ... ); @@ -23,13 +23,9 @@ for iIteration = 1:numIterations pause(0.1); - obj.step([], wasSuccessful(iIteration), []); + b([], wasSuccessful(iIteration), []); end -obj.release(); - - - +b.release(); -% End of file: g_PassSuccessInfo_demo.m diff --git a/demos/f_PrintInfoDuringRun_demo.m b/demos/printInfoDuringRun.m similarity index 50% rename from demos/f_PrintInfoDuringRun_demo.m rename to demos/printInfoDuringRun.m index 6938ab0..613580b 100644 --- a/demos/f_PrintInfoDuringRun_demo.m +++ b/demos/printInfoDuringRun.m @@ -1,32 +1,29 @@ -% Demo of the printMessage() method. In 3 iterations an info message is -% printed +% Demo of the printMessage() method. In 3 iterations an info message is printed. % % Author: J.-A. Adrian (JA) -% Date : 21-Jun-2016 17:48:22 % +clear; +close all; addpath('..'); numIterations = 1e2; -%% Simple setup and print a message in desired iterations -obj = ProgressBar(numIterations, ... +%% Simple Setup and Print a Message in Desired Iterations +b = ProgressBar(numIterations, ... 'Title', 'Progress' ... ); for iIteration = 1:numIterations - obj.step(1, [], []); + b(1, [], []); if iIteration == 30 || iIteration == 50 || iIteration == 70 - obj.printMessage(sprintf('Hello! @Iteration %i', iIteration)); + b.printMessage(sprintf('Hello! @Iteration %i', iIteration)); end - pause(1e-1); + pause(0.1); end -obj.release(); +b.release(); - - -% End of file: f_PrintInfoDuringRun_demo.m diff --git a/demos/h_titleBanner.m b/demos/titleBanner.m similarity index 55% rename from demos/h_titleBanner.m rename to demos/titleBanner.m index 5370e73..b1f0a4a 100644 --- a/demos/h_titleBanner.m +++ b/demos/titleBanner.m @@ -1,39 +1,38 @@ -% +% If the title is chosen very long it will work as a banner, rotating each step a bit further. % % Author: J.-A. Adrian (JA) -% Date : 14-Apr-2017 21:49:26 % +clear; +close all; addpath('..'); -numIterations = 100; +numIterations = 50; %% Short Title -obj = ProgressBar(numIterations, ... +b = ProgressBar(numIterations, ... 'Title', 'Short' ... ); for iIteration = 1:numIterations pause(0.1); - obj.step(1, [], []); + b(1, [], []); end -obj.release(); +b.release(); %% Long Title numIterations = 100; -obj = ProgressBar(numIterations, ... +b = ProgressBar(numIterations, ... 'Title', 'A Long Long Long Title' ... ); for iIteration = 1:numIterations pause(0.1); - obj.step(1, [], []); + b(1, [], []); end -obj.release(); - +b.release(); -% End of file: h_titleBanner.m From 8961069d280440927fa9f2a63014a4ef034c8613 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 21:26:30 +0100 Subject: [PATCH 28/30] Bump version --- CHANGELOG.md | 2 +- ProgressBar.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c90fec..50f3cf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ 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). -## [Unreleased] +## [3.2.0] - 2020-11-14 ### 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` - Proper unit testing as a test-case class diff --git a/ProgressBar.m b/ProgressBar.m index 694fc44..538ff8b 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.1.0'; + VERSION = '3.2.0'; end properties (Nontunable) From 09e26284ed0af5303479b9addce5ecdb474a3a86 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 21:28:46 +0100 Subject: [PATCH 29/30] Minor Change in docstring --- progress.m | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/progress.m b/progress.m index c77ac7d..9b964c8 100644 --- a/progress.m +++ b/progress.m @@ -15,16 +15,13 @@ % Example: % % for k = progress(1:100) - % % do some processing + % % do some processing % end % % Or with additional name-value pairs: % - % for k = progress(1:100, ... - % 'Title', 'Computing...' ... - % ) - % - % % do some processing + % for k = progress(1:100, 'Title', 'Computing') + % % do some processing % end % % ========================================================================= From 156e4e15274956d7a9108635e7a3e511814c4761 Mon Sep 17 00:00:00 2001 From: Jens-Alrik Adrian Date: Fri, 13 Nov 2020 21:31:38 +0100 Subject: [PATCH 30/30] Update release date --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50f3cf2..299cf9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ 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.2.0] - 2020-11-14 +## [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` - Proper unit testing as a test-case class @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Progress bar in non-finite and iteration mode (not having a konwn total number of iterations) will now show correct unit string `it` instead of toggling wrongly between `it` and `s` + ## [3.1.1] - 2019-11-26 ### Fixed - Updated CHANGELOG @@ -22,12 +23,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - MATHWORKS FileExchange banner in the README + ## [3.1.0] - 2019-11-03 ### Changed - Made some properties constant and method static - Cleaned up comments - Updated README and CHANGELOG + ## [3.0.0] - 2017-05-02 ### Changed The whole class has been re-factored to be a **MATLAB System Object** @@ -35,6 +38,7 @@ The whole class has been re-factored to be a **MATLAB System Object** - Due to the System Object class structure, multiple methods have been renamed and optional input arguments for the `step()` (formerly `update()`) method are now mandatory. See the example in `README.md`. - The bar's **title** is now mandatory and has a default string: `'Processing'`. Notable change is that if the title exceeds the length of 20 characters the title will act like a banner and cycle with a shift of 3 characters each time the bar is updated. This way, the progress bar can have a constant width (for now, 90 characters seem to fit many screens). + ## [3.1.0] - 2019-11-03 ### Fixed - Moved setup-related method-calls to the setup phase of the object. In v3.0.0, the timer object was initialized in the constructor, leading to potential issues in computing the elapsed time and estimated time until completion. Also, user-set properties (like, e.g., the bar's title) could have had no influence while calling the step function due to the same reason.