Home > Codi > progressbar.m

progressbar

PURPOSE ^

Description:

SYNOPSIS ^

function progressbar(varargin)

DESCRIPTION ^

 Description:
   progressbar() provides an indication of the progress of some task using
 graphics and text. Calling progressbar repeatedly will update the figure and
 automatically estimate the amount of time remaining.
   This implementation of progressbar is intended to be extremely simple to use
 while providing a high quality user experience.

 Features:
   - Can add progressbar to existing m-files with a single line of code.
   - Supports multiple bars in one figure to show progress of nested loops.
   - Optional labels on bars.
   - Figure closes automatically when task is complete.
   - Only one figure can exist so old figures don't clutter the desktop.
   - Remaining time estimate is accurate even if the figure gets closed.
   - Minimal execution time. Won't slow down code.
   - Randomized color. When a programmer gets bored...

 Example Function Calls For Single Bar Usage:
   progressbar               % Initialize/reset
   progressbar(0)            % Initialize/reset
   progressbar('Label')      % Initialize/reset and label the bar
   progressbar(0.5)          % Update
   progressbar(1)            % Close

 Example Function Calls For Multi Bar Usage:
   progressbar(0, 0)         % Initialize/reset two bars
   progressbar('A', '')      % Initialize/reset two bars with one label
   progressbar('', 'B')      % Initialize/reset two bars with one label
   progressbar('A', 'B')     % Initialize/reset two bars with two labels
   progressbar(0.3)          % Update 1st bar
   progressbar(0.3, [])      % Update 1st bar
   progressbar([], 0.3)      % Update 2nd bar
   progressbar(0.7, 0.9)     % Update both bars
   progressbar(1)            % Close
   progressbar(1, [])        % Close
   progressbar(1, 0.4)       % Close

 Notes:
   For best results, call progressbar with all zero (or all string) inputs
 before any processing. This sets the proper starting time reference to
 calculate time remaining.
   Bar color is choosen randomly when the figure is created or reset. Clicking
 the bar will cause a random color change.

 Demos:
     % Single bar
     m = 500;
     progressbar % Init single bar
     for i = 1:m
       pause(0.01) % Do something important
       progressbar(i/m) % Update progress bar
     end
 
     % Simple multi bar (update one bar at a time)
     m = 4;
     n = 3;
     p = 100;
     progressbar(0,0,0) % Init 3 bars
     for i = 1:m
         progressbar([],0) % Reset 2nd bar
         for j = 1:n
             progressbar([],[],0) % Reset 3rd bar
             for k = 1:p
                 pause(0.01) % Do something important
                 progressbar([],[],k/p) % Update 3rd bar
             end
             progressbar([],j/n) % Update 2nd bar
         end
         progressbar(i/m) % Update 1st bar
     end
 
     % Fancy multi bar (use labels and update all bars at once)
     m = 4;
     n = 3;
     p = 100;
     progressbar('Monte Carlo Trials','Simulation','Component') % Init 3 bars
     for i = 1:m
         for j = 1:n
             for k = 1:p
                 pause(0.01) % Do something important
                 % Update all bars
                 frac3 = k/p;
                 frac2 = ((j-1) + frac3) / n;
                 frac1 = ((i-1) + frac2) / m;
                 progressbar(frac1, frac2, frac3)
             end
         end
     end

 Author:
   Steve Hoelzer

 Revisions:
 2002-Feb-27   Created function
 2002-Mar-19   Updated title text order
 2002-Apr-11   Use floor instead of round for percentdone
 2002-Jun-06   Updated for speed using patch (Thanks to waitbar.m)
 2002-Jun-19   Choose random patch color when a new figure is created
 2002-Jun-24   Click on bar or axes to choose new random color
 2002-Jun-27   Calc time left, reset progress bar when fractiondone == 0
 2002-Jun-28   Remove extraText var, add position var
 2002-Jul-18   fractiondone input is optional
 2002-Jul-19   Allow position to specify screen coordinates
 2002-Jul-22   Clear vars used in color change callback routine
 2002-Jul-29   Position input is always specified in pixels
 2002-Sep-09   Change order of title bar text
 2003-Jun-13   Change 'min' to 'm' because of built in function 'min'
 2003-Sep-08   Use callback for changing color instead of string
 2003-Sep-10   Use persistent vars for speed, modify titlebarstr
 2003-Sep-25   Correct titlebarstr for 0% case
 2003-Nov-25   Clear all persistent vars when percentdone = 100
 2004-Jan-22   Cleaner reset process, don't create figure if percentdone = 100
 2004-Jan-27   Handle incorrect position input
 2004-Feb-16   Minimum time interval between updates
 2004-Apr-01   Cleaner process of enforcing minimum time interval
 2004-Oct-08   Seperate function for timeleftstr, expand to include days
 2004-Oct-20   Efficient if-else structure for sec2timestr
 2006-Sep-11   Width is a multiple of height (don't stretch on widescreens)
 2010-Sep-21   Major overhaul to support multiple bars and add labels

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function progressbar(varargin)
0002 % Description:
0003 %   progressbar() provides an indication of the progress of some task using
0004 % graphics and text. Calling progressbar repeatedly will update the figure and
0005 % automatically estimate the amount of time remaining.
0006 %   This implementation of progressbar is intended to be extremely simple to use
0007 % while providing a high quality user experience.
0008 %
0009 % Features:
0010 %   - Can add progressbar to existing m-files with a single line of code.
0011 %   - Supports multiple bars in one figure to show progress of nested loops.
0012 %   - Optional labels on bars.
0013 %   - Figure closes automatically when task is complete.
0014 %   - Only one figure can exist so old figures don't clutter the desktop.
0015 %   - Remaining time estimate is accurate even if the figure gets closed.
0016 %   - Minimal execution time. Won't slow down code.
0017 %   - Randomized color. When a programmer gets bored...
0018 %
0019 % Example Function Calls For Single Bar Usage:
0020 %   progressbar               % Initialize/reset
0021 %   progressbar(0)            % Initialize/reset
0022 %   progressbar('Label')      % Initialize/reset and label the bar
0023 %   progressbar(0.5)          % Update
0024 %   progressbar(1)            % Close
0025 %
0026 % Example Function Calls For Multi Bar Usage:
0027 %   progressbar(0, 0)         % Initialize/reset two bars
0028 %   progressbar('A', '')      % Initialize/reset two bars with one label
0029 %   progressbar('', 'B')      % Initialize/reset two bars with one label
0030 %   progressbar('A', 'B')     % Initialize/reset two bars with two labels
0031 %   progressbar(0.3)          % Update 1st bar
0032 %   progressbar(0.3, [])      % Update 1st bar
0033 %   progressbar([], 0.3)      % Update 2nd bar
0034 %   progressbar(0.7, 0.9)     % Update both bars
0035 %   progressbar(1)            % Close
0036 %   progressbar(1, [])        % Close
0037 %   progressbar(1, 0.4)       % Close
0038 %
0039 % Notes:
0040 %   For best results, call progressbar with all zero (or all string) inputs
0041 % before any processing. This sets the proper starting time reference to
0042 % calculate time remaining.
0043 %   Bar color is choosen randomly when the figure is created or reset. Clicking
0044 % the bar will cause a random color change.
0045 %
0046 % Demos:
0047 %     % Single bar
0048 %     m = 500;
0049 %     progressbar % Init single bar
0050 %     for i = 1:m
0051 %       pause(0.01) % Do something important
0052 %       progressbar(i/m) % Update progress bar
0053 %     end
0054 %
0055 %     % Simple multi bar (update one bar at a time)
0056 %     m = 4;
0057 %     n = 3;
0058 %     p = 100;
0059 %     progressbar(0,0,0) % Init 3 bars
0060 %     for i = 1:m
0061 %         progressbar([],0) % Reset 2nd bar
0062 %         for j = 1:n
0063 %             progressbar([],[],0) % Reset 3rd bar
0064 %             for k = 1:p
0065 %                 pause(0.01) % Do something important
0066 %                 progressbar([],[],k/p) % Update 3rd bar
0067 %             end
0068 %             progressbar([],j/n) % Update 2nd bar
0069 %         end
0070 %         progressbar(i/m) % Update 1st bar
0071 %     end
0072 %
0073 %     % Fancy multi bar (use labels and update all bars at once)
0074 %     m = 4;
0075 %     n = 3;
0076 %     p = 100;
0077 %     progressbar('Monte Carlo Trials','Simulation','Component') % Init 3 bars
0078 %     for i = 1:m
0079 %         for j = 1:n
0080 %             for k = 1:p
0081 %                 pause(0.01) % Do something important
0082 %                 % Update all bars
0083 %                 frac3 = k/p;
0084 %                 frac2 = ((j-1) + frac3) / n;
0085 %                 frac1 = ((i-1) + frac2) / m;
0086 %                 progressbar(frac1, frac2, frac3)
0087 %             end
0088 %         end
0089 %     end
0090 %
0091 % Author:
0092 %   Steve Hoelzer
0093 %
0094 % Revisions:
0095 % 2002-Feb-27   Created function
0096 % 2002-Mar-19   Updated title text order
0097 % 2002-Apr-11   Use floor instead of round for percentdone
0098 % 2002-Jun-06   Updated for speed using patch (Thanks to waitbar.m)
0099 % 2002-Jun-19   Choose random patch color when a new figure is created
0100 % 2002-Jun-24   Click on bar or axes to choose new random color
0101 % 2002-Jun-27   Calc time left, reset progress bar when fractiondone == 0
0102 % 2002-Jun-28   Remove extraText var, add position var
0103 % 2002-Jul-18   fractiondone input is optional
0104 % 2002-Jul-19   Allow position to specify screen coordinates
0105 % 2002-Jul-22   Clear vars used in color change callback routine
0106 % 2002-Jul-29   Position input is always specified in pixels
0107 % 2002-Sep-09   Change order of title bar text
0108 % 2003-Jun-13   Change 'min' to 'm' because of built in function 'min'
0109 % 2003-Sep-08   Use callback for changing color instead of string
0110 % 2003-Sep-10   Use persistent vars for speed, modify titlebarstr
0111 % 2003-Sep-25   Correct titlebarstr for 0% case
0112 % 2003-Nov-25   Clear all persistent vars when percentdone = 100
0113 % 2004-Jan-22   Cleaner reset process, don't create figure if percentdone = 100
0114 % 2004-Jan-27   Handle incorrect position input
0115 % 2004-Feb-16   Minimum time interval between updates
0116 % 2004-Apr-01   Cleaner process of enforcing minimum time interval
0117 % 2004-Oct-08   Seperate function for timeleftstr, expand to include days
0118 % 2004-Oct-20   Efficient if-else structure for sec2timestr
0119 % 2006-Sep-11   Width is a multiple of height (don't stretch on widescreens)
0120 % 2010-Sep-21   Major overhaul to support multiple bars and add labels
0121 %
0122 
0123 persistent progfig progdata lastupdate
0124 
0125 % Get inputs
0126 if nargin > 0
0127     input = varargin;
0128     ninput = nargin;
0129 else
0130     % If no inputs, init with a single bar
0131     input = {0};
0132     ninput = 1;
0133 end
0134 
0135 % If task completed, close figure and clear vars, then exit
0136 if input{1} == 1
0137     if ishandle(progfig)
0138         delete(progfig) % Close progress bar
0139     end
0140     clear progfig progdata lastupdate % Clear persistent vars
0141     drawnow
0142     return
0143 end
0144 
0145 % Init reset flag
0146 resetflag = false;
0147 
0148 % Set reset flag if first input is a string
0149 if ischar(input{1})
0150     resetflag = true;
0151 end
0152 
0153 % Set reset flag if all inputs are zero
0154 if input{1} == 0
0155     % If the quick check above passes, need to check all inputs
0156     if all([input{:}] == 0) && (length([input{:}]) == ninput)
0157         resetflag = true;
0158     end
0159 end
0160 
0161 % Set reset flag if more inputs than bars
0162 if ninput > length(progdata)
0163     resetflag = true;
0164 end
0165 
0166 % If reset needed, close figure and forget old data
0167 if resetflag
0168     if ishandle(progfig)
0169         delete(progfig) % Close progress bar
0170     end
0171     progfig = [];
0172     progdata = []; % Forget obsolete data
0173 end
0174 
0175 % Create new progress bar if needed
0176 if ishandle(progfig)
0177 else % This strange if-else works when progfig is empty (~ishandle() does not)
0178     
0179     % Define figure size and axes padding for the single bar case
0180     height = 0.03;
0181     width = height * 8;
0182     hpad = 0.02;
0183     vpad = 0.25;
0184     
0185     % Figure out how many bars to draw
0186     nbars = max(ninput, length(progdata));
0187     
0188     % Adjust figure size and axes padding for number of bars
0189     heightfactor = (1 - vpad) * nbars + vpad;
0190     height = height * heightfactor;
0191     vpad = vpad / heightfactor;
0192     
0193     % Initialize progress bar figure
0194     left = (1 - width) / 2;
0195     bottom = (1 - height) / 2;
0196     progfig = figure(...
0197         'Units', 'normalized',...
0198         'Position', [left bottom width height],...
0199         'NumberTitle', 'off',...
0200         'Resize', 'off',...
0201         'MenuBar', 'none' );
0202     
0203     % Initialize axes, patch, and text for each bar
0204     left = hpad;
0205     width = 1 - 2*hpad;
0206     vpadtotal = vpad * (nbars + 1);
0207     height = (1 - vpadtotal) / nbars;
0208     for ndx = 1:nbars
0209         % Create axes, patch, and text
0210         bottom = vpad + (vpad + height) * (nbars - ndx);
0211         progdata(ndx).progaxes = axes( ...
0212             'Position', [left bottom width height], ...
0213             'XLim', [0 1], ...
0214             'YLim', [0 1], ...
0215             'Box', 'on', ...
0216             'ytick', [], ...
0217             'xtick', [] );
0218         progdata(ndx).progpatch = patch( ...
0219             'XData', [0 0 0 0], ...
0220             'YData', [0 0 1 1] );
0221         progdata(ndx).progtext = text(0.99, 0.5, '', ...
0222             'HorizontalAlignment', 'Right', ...
0223             'FontUnits', 'Normalized', ...
0224             'FontSize', 0.7 );
0225         progdata(ndx).proglabel = text(0.01, 0.5, '', ...
0226             'HorizontalAlignment', 'Left', ...
0227             'FontUnits', 'Normalized', ...
0228             'FontSize', 0.7 );
0229         if ischar(input{ndx})
0230             set(progdata(ndx).proglabel, 'String', input{ndx})
0231             input{ndx} = 0;
0232         end
0233         
0234         % Set callbacks to change color on mouse click
0235         set(progdata(ndx).progaxes, 'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
0236         set(progdata(ndx).progpatch, 'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
0237         set(progdata(ndx).progtext, 'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
0238         set(progdata(ndx).proglabel, 'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
0239         
0240         % Pick a random color for this patch
0241         changecolor([], [], progdata(ndx).progpatch)
0242         
0243         % Set starting time reference
0244         if ~isfield(progdata(ndx), 'starttime') || isempty(progdata(ndx).starttime)
0245             progdata(ndx).starttime = clock;
0246         end
0247     end
0248     
0249     % Set time of last update to ensure a redraw
0250     lastupdate = clock - 1;
0251     
0252 end
0253 
0254 % Process inputs and update state of progdata
0255 for ndx = 1:ninput
0256     if ~isempty(input{ndx})
0257         progdata(ndx).fractiondone = input{ndx};
0258         progdata(ndx).clock = clock;
0259     end
0260 end
0261 
0262 % Enforce a minimum time interval between graphics updates
0263 myclock = clock;
0264 if abs(myclock(6) - lastupdate(6)) < 0.01 % Could use etime() but this is faster
0265     return
0266 end
0267 
0268 % Update progress patch
0269 for ndx = 1:length(progdata)
0270     set(progdata(ndx).progpatch, 'XData', ...
0271         [0, progdata(ndx).fractiondone, progdata(ndx).fractiondone, 0])
0272 end
0273 
0274 % Update progress text if there is more than one bar
0275 if length(progdata) > 1
0276     for ndx = 1:length(progdata)
0277         set(progdata(ndx).progtext, 'String', ...
0278             sprintf('%1d%%', floor(100*progdata(ndx).fractiondone)))
0279     end
0280 end
0281 
0282 % Update progress figure title bar
0283 if progdata(1).fractiondone > 0
0284     runtime = etime(progdata(1).clock, progdata(1).starttime);
0285     timeleft = runtime / progdata(1).fractiondone - runtime;
0286     timeleftstr = sec2timestr(timeleft);
0287     titlebarstr = sprintf('%2d%%    %s remaining', ...
0288         floor(100*progdata(1).fractiondone), timeleftstr);
0289 else
0290     titlebarstr = ' 0%';
0291 end
0292 set(progfig, 'Name', titlebarstr)
0293 
0294 % Force redraw to show changes
0295 drawnow
0296 
0297 % Record time of this update
0298 lastupdate = clock;
0299 
0300 
0301 % ------------------------------------------------------------------------------
0302 function changecolor(h, e, progpatch) %#ok<INUSL>
0303 % Change the color of the progress bar patch
0304 
0305 % Prevent color from being too dark or too light
0306 colormin = 1.5;
0307 colormax = 2.8;
0308 
0309 thiscolor = rand(1, 3);
0310 while (sum(thiscolor) < colormin) || (sum(thiscolor) > colormax)
0311     thiscolor = rand(1, 3);
0312 end
0313 
0314 set(progpatch, 'FaceColor', thiscolor)
0315 
0316 
0317 % ------------------------------------------------------------------------------
0318 function timestr = sec2timestr(sec)
0319 % Convert a time measurement from seconds into a human readable string.
0320 
0321 % Convert seconds to other units
0322 w = floor(sec/604800); % Weeks
0323 sec = sec - w*604800;
0324 d = floor(sec/86400); % Days
0325 sec = sec - d*86400;
0326 h = floor(sec/3600); % Hours
0327 sec = sec - h*3600;
0328 m = floor(sec/60); % Minutes
0329 sec = sec - m*60;
0330 s = floor(sec); % Seconds
0331 
0332 % Create time string
0333 if w > 0
0334     if w > 9
0335         timestr = sprintf('%d week', w);
0336     else
0337         timestr = sprintf('%d week, %d day', w, d);
0338     end
0339 elseif d > 0
0340     if d > 9
0341         timestr = sprintf('%d day', d);
0342     else
0343         timestr = sprintf('%d day, %d hr', d, h);
0344     end
0345 elseif h > 0
0346     if h > 9
0347         timestr = sprintf('%d hr', h);
0348     else
0349         timestr = sprintf('%d hr, %d min', h, m);
0350     end
0351 elseif m > 0
0352     if m > 9
0353         timestr = sprintf('%d min', m);
0354     else
0355         timestr = sprintf('%d min, %d sec', m, s);
0356     end
0357 else
0358     timestr = sprintf('%d sec', s);
0359 end

Generated on Wed 12-Sep-2012 13:03:54 by m2html © 2005