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
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