Skip to content

Instantly share code, notes, and snippets.

@lukegalea
Created July 21, 2008 15:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukegalea/58 to your computer and use it in GitHub Desktop.
Save lukegalea/58 to your computer and use it in GitHub Desktop.
%% Copyright (c) 2008 Luke Galea www.ideaforge.org
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
%% in the Software without restriction, including without limitation the rights
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%% THE SOFTWARE.
%% Progress Bar - v0.2
-module(progress_bar).
-export([start/2, finish/1, increment/1, start_server/2, increment_server/0,
finish_server/0, example/1, example_processes/2]).
-record(progress_bar, { title, total, current, start_time }).
-define(bar, "oooooooooooooooooooooooooooooooooooooooo").
-define(percent(Current, Total), round((Current/Total) * 100)).
start(Title, Total) ->
PBar = #progress_bar{ title = Title, total = Total, current = 0, start_time = current_time() },
show(PBar),
PBar.
increment(PBar = #progress_bar{ current = Total, total = Total }) ->
finish(PBar);
increment(PBar = #progress_bar{ current = Current, total = Total })
when ?percent(Current, Total) =:= ?percent(Current + 1, Total) ->
PBar#progress_bar{ current = Current + 1 }; %% Only display if there has been a change in percent
increment(PBar = #progress_bar{ current = Current }) ->
show(PBar1 = PBar#progress_bar{ current = Current + 1 }),
PBar1.
finish(PBar = #progress_bar{ total = Total }) ->
show( PBar#progress_bar{ current = Total } ),
io:format("\n").
show(#progress_bar{ title = Title, total = Total, current = Current, start_time = StartTime }) ->
Percent = ?percent(Current, Total),
io:format("~-12s ~3B% ~s ETA: ~s\r", [Title, Percent, bar(Percent), eta(Current, Total, StartTime)]).
%%Truncate the bar string, then pad it to the proper width with spaces
bar(Percent) ->
BarLength = length(?bar),
io_lib:format("~-*s", [BarLength, io_lib:format("~*s", [Percent * BarLength div 100, ?bar] )]).
current_time() ->
calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(now())).
eta( 0, _, _ ) -> "--:--:--";
eta( Current, Total, StartTime ) ->
Elapsed = current_time() - StartTime,
{_,{Hours,Mins,Secs}} = calendar:gregorian_seconds_to_datetime( Elapsed * Total div Current - Elapsed ),
io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [Hours,Mins,Secs]).
%%%%%%%%%%%%%%%%%%%
start_server( Title, Total ) ->
Caller = self(),
register(progress_bar, spawn_link( fun() -> server(Caller, start( Title, Total ) ) end ) ).
finish_server() -> progress_bar ! finish.
increment_server() -> progress_bar ! increment.
server( Caller, PBar = #progress_bar{ current = Total, total = Total } ) ->
PBar:finish(),
Caller ! finished;
server( Caller, PBar ) ->
receive
finish ->
PBar:finish(),
Caller ! finished;
increment ->
server( Caller, PBar:increment() )
end.
%%%%%%%%% Example App %%%%%%%%%
example(N) ->
PBar = start("Example", N),
example_work(N, PBar),
PBar:finish().
example_work(0,_) -> [];
example_work(N, PBar) ->
timer:sleep(100),
example_work(N-1, PBar:increment()).
%%%%%%%%%%%%%%%%%%%%%%%%
example_processes(N, P) when N rem P =/= 0 -> exit( "N must be divisible by P" );
example_processes(N, P) ->
start_server("Example", N),
for(1, P, fun() -> spawn_link( fun() -> example_work_process(N div P) end ) end ),
receive
finished -> finished
end.
example_work_process(0) -> void;
example_work_process(N) ->
timer:sleep(100),
increment_server(),
example_work_process(N-1).
for(N, N, F) -> [F()];
for(I, N, F) -> [F()|for(I+1, N, F)].
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment