1 function progressbar(varargin)
3 % progressbar() provides an indication of the progress of some task using
4 % graphics and text. Calling progressbar repeatedly will update the figure and
5 % automatically estimate the amount of time remaining.
6 % This implementation of progressbar is intended to be extremely simple to use
7 % while providing a high quality user experience.
10 % - Can add progressbar to existing m-files with a single line of code.
11 % - Supports multiple bars in one figure to show progress of nested loops.
12 % - Optional labels on bars.
13 % - Figure closes automatically when task is complete.
14 % - Only one figure can exist so old figures don't clutter the desktop.
15 % - Remaining time estimate is accurate even if the figure gets closed.
16 % - Minimal execution time. Won't slow down code.
17 % - Randomized color. When a programmer gets bored...
19 % Example Function Calls For Single Bar Usage:
20 % progressbar % Initialize/reset
21 % progressbar(0) % Initialize/reset
22 % progressbar('Label') % Initialize/reset and label the bar
23 % progressbar(0.5) % Update
24 % progressbar(1) % Close
26 % Example Function Calls For Multi Bar Usage:
27 % progressbar(0, 0) % Initialize/reset two bars
28 % progressbar('A', '') % Initialize/reset two bars with one label
29 % progressbar('', 'B') % Initialize/reset two bars with one label
30 % progressbar('A', 'B') % Initialize/reset two bars with two labels
31 % progressbar(0.3) % Update 1st bar
32 % progressbar(0.3, []) % Update 1st bar
33 % progressbar([], 0.3) % Update 2nd bar
34 % progressbar(0.7, 0.9) % Update both bars
35 % progressbar(1) % Close
36 % progressbar(1, []) % Close
37 % progressbar(1, 0.4) % Close
40 % For best results, call progressbar with all zero (or all
string) inputs
41 % before any processing. This sets the proper starting time reference to
42 % calculate time remaining.
43 % Bar color is choosen randomly when the figure is created or reset. Clicking
44 % the bar will cause a random color change.
49 % progressbar % Init single bar
51 % pause(0.01) % Do something important
52 % progressbar(i/m) % Update progress bar
55 % % Simple multi bar (update one bar at a time)
59 % progressbar(0,0,0) % Init 3 bars
61 % progressbar([],0) % Reset 2nd bar
63 % progressbar([],[],0) % Reset 3rd bar
65 % pause(0.01) % Do something important
66 % progressbar([],[],k/p) % Update 3rd bar
68 % progressbar([],j/n) % Update 2nd bar
70 % progressbar(i/m) % Update 1st bar
73 % % Fancy multi bar (use labels and update all bars at once)
77 % progressbar('Monte Carlo Trials','Simulation','Component') % Init 3 bars
81 % pause(0.01) % Do something important
84 % frac2 = ((j-1) + frac3) / n;
85 % frac1 = ((i-1) + frac2) / m;
86 % progressbar(frac1, frac2, frac3)
95 % 2002-Feb-27 Created function
96 % 2002-Mar-19 Updated title text order
97 % 2002-Apr-11 Use floor instead of round for percentdone
98 % 2002-Jun-06 Updated for speed using patch (Thanks to waitbar.m)
99 % 2002-Jun-19 Choose random patch color when a new figure is created
100 % 2002-Jun-24 Click on bar or axes to choose new random color
101 % 2002-Jun-27 Calc time left, reset progress bar when fractiondone == 0
102 % 2002-Jun-28 Remove extraText var, add position var
103 % 2002-Jul-18 fractiondone input is optional
104 % 2002-Jul-19 Allow position to specify screen coordinates
105 % 2002-Jul-22 Clear vars used in color change callback routine
106 % 2002-Jul-29 Position input is always specified in pixels
107 % 2002-Sep-09 Change order of title bar text
108 % 2003-Jun-13 Change 'min' to 'm' because of built in function 'min'
109 % 2003-Sep-08 Use callback for changing color instead of
string 110 % 2003-Sep-10 Use persistent vars for speed, modify titlebarstr
111 % 2003-Sep-25 Correct titlebarstr for 0% case
112 % 2003-Nov-25 Clear all persistent vars when percentdone = 100
113 % 2004-Jan-22 Cleaner reset process, don't create figure if percentdone = 100
114 % 2004-Jan-27 Handle incorrect position input
115 % 2004-Feb-16 Minimum time interval between updates
116 % 2004-Apr-01 Cleaner process of enforcing minimum time interval
117 % 2004-Oct-08 Seperate function for timeleftstr, expand to include days
118 % 2004-Oct-20 Efficient if-else structure for sec2timestr
119 % 2006-Sep-11 Width is a multiple of height (don't stretch on widescreens)
120 % 2010-Sep-21 Major overhaul to support multiple bars and add labels
123 persistent progfig progdata lastupdate
130 % If no inputs, init with a single bar
135 % If task completed, close figure and clear vars, then exit
138 delete(progfig) % Close progress bar
140 clear progfig progdata lastupdate % Clear persistent vars
148 % Set reset flag if first input is a
string 153 % Set reset flag
if all inputs are zero
155 % If the quick check above passes, need to check all inputs
156 if all([input{:}] == 0) && (length([input{:}]) == ninput)
161 % Set reset flag if more inputs than bars
162 if ninput > length(progdata)
166 % If reset needed, close figure and forget old data
169 delete(progfig) % Close progress bar
172 progdata = []; % Forget obsolete data
175 % Create new progress bar if needed
177 else % This strange if-else works when progfig is empty (~ishandle() does not)
179 % Define figure size and axes padding for the single bar case
185 % Figure out how many bars to draw
186 nbars = max(ninput, length(progdata));
188 % Adjust figure size and axes padding for number of bars
189 heightfactor = (1 - vpad) * nbars + vpad;
190 height = height * heightfactor;
191 vpad = vpad / heightfactor;
193 % Initialize progress bar figure
194 left = (1 - width) / 2;
195 bottom = (1 - height) / 2;
197 'Units', 'normalized',...
198 'Position', [left bottom width height],...
199 'NumberTitle', 'off',...
203 % Initialize axes, patch, and text for each bar
206 vpadtotal = vpad * (nbars + 1);
207 height = (1 - vpadtotal) / nbars;
209 % Create axes, patch, and text
210 bottom = vpad + (vpad + height) * (nbars - ndx);
211 progdata(ndx).progaxes = axes( ...
212 'Position', [left bottom width height], ...
218 progdata(ndx).progpatch = patch( ...
219 'XData', [0 0 0 0], ...
220 'YData', [0 0 1 1] );
221 progdata(ndx).progtext = text(0.99, 0.5, '', ...
222 'HorizontalAlignment', 'Right', ...
223 'FontUnits', 'Normalized', ...
225 progdata(ndx).proglabel = text(0.01, 0.5, '', ...
226 'HorizontalAlignment', 'Left', ...
227 'FontUnits', 'Normalized', ...
229 if ischar(input{ndx})
230 set(progdata(ndx).proglabel,
'String', input{ndx})
234 % Set callbacks to change color on mouse click
235 set(progdata(ndx).progaxes,
'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
236 set(progdata(ndx).progpatch,
'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
237 set(progdata(ndx).progtext,
'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
238 set(progdata(ndx).proglabel,
'ButtonDownFcn', {@changecolor, progdata(ndx).progpatch})
240 % Pick a random color
for this patch
241 changecolor([], [], progdata(ndx).progpatch)
243 % Set starting time reference
244 if ~isfield(progdata(ndx),
'starttime') || isempty(progdata(ndx).starttime)
245 progdata(ndx).starttime = clock;
249 % Set time of last update to ensure a redraw
250 lastupdate = clock - 1;
254 % Process inputs and update state of progdata
256 if ~isempty(input{ndx})
257 progdata(ndx).fractiondone = input{ndx};
258 progdata(ndx).clock = clock;
262 % Enforce a minimum time interval between graphics updates
264 if abs(myclock(6) - lastupdate(6)) < 0.01 % Could use etime() but this is faster
268 % Update progress patch
269 for ndx = 1:length(progdata)
270 set(progdata(ndx).progpatch, 'XData', ...
271 [0, progdata(ndx).fractiondone, progdata(ndx).fractiondone, 0])
274 % Update progress text if there is more than one bar
275 if length(progdata) > 1
276 for ndx = 1:length(progdata)
277 set(progdata(ndx).progtext, 'String', ...
278 sprintf('%1d%%', floor(100*progdata(ndx).fractiondone)))
282 % Update progress figure title bar
283 if progdata(1).fractiondone > 0
284 runtime = etime(progdata(1).clock, progdata(1).starttime);
285 timeleft = runtime / progdata(1).fractiondone - runtime;
286 timeleftstr = sec2timestr(timeleft);
287 titlebarstr = sprintf('%2d%% %s remaining', ...
288 floor(100*progdata(1).fractiondone), timeleftstr);
292 set(progfig, 'Name', titlebarstr)
294 % Force redraw to show changes
297 % Record time of this update
301 % ------------------------------------------------------------------------------
302 function changecolor(h, e, progpatch) %
#ok<INUSL> 303 % Change the color of the progress bar patch
305 % Prevent color from being too dark or too light
309 thiscolor = rand(1, 3);
310 while (sum(thiscolor) < colormin) || (sum(thiscolor) > colormax)
311 thiscolor = rand(1, 3);
314 set(progpatch,
'FaceColor', thiscolor)
317 % ------------------------------------------------------------------------------
318 function timestr = sec2timestr(sec)
319 % Convert a time measurement from seconds into a human readable
string.
321 % Convert seconds to other units
322 w = floor(sec/604800); % Weeks
323 sec = sec - w*604800;
324 d = floor(sec/86400); % Days
326 h = floor(sec/3600); % Hours
328 m = floor(sec/60); % Minutes
330 s = floor(sec); % Seconds
335 timestr = sprintf(
'%d week', w);
337 timestr = sprintf(
'%d week, %d day', w, d);
341 timestr = sprintf(
'%d day', d);
343 timestr = sprintf(
'%d day, %d hr', d, h);
347 timestr = sprintf(
'%d hr', h);
349 timestr = sprintf(
'%d hr, %d min', h, m);
353 timestr = sprintf(
'%d min', m);
355 timestr = sprintf(
'%d min, %d sec', m, s);
358 timestr = sprintf(
'%d sec', s);