%% 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)].