You are currently browsing the archives for the progress bar tag.

Progress Bar in OCaml

The really annoying thing when I write programs which take a few minutes to complete some task is to know what amount of data has already been treated. Usually I use some text output to do so: the easiest solution is to compute the total number of steps (for eample the number of results in a MySQL query, or the number of elements in an array, etc.) and then at each step print the percentage of tasks already computed.

This works quite well, except your terminal gets filled with 1% 2% 3% 4% … and it's even worse if you add some line breaks.

The solution I use in OCaml is the following: a progress bar ala wget. In order to do that, I'll use two magical functions (which only work on ANSI terminals) :

let save_cursor () =
  print_string "\027[s"
let restore_cursor () =
  print_string "\027[u"

The first function saves the current location of the cursor and the second puts the cursor back to the saved position. The idea is pretty straightforward: first we save the current location, then we print the progress bar, then come back to the saved position, and start over.

I've written a quick module which help to achieve this (it does not deal with terminal resizing: the width of the progress bar will be around ~110 characters).

let width = 100

let total = ref 0
let state = ref 0
let last_up = ref 0

let save_cursor () =
  print_string "\027[s"
let restore_cursor () =
  print_string "\027[u"

let init t =
  total := t;
  state := 0;
  last_up := 0;
  save_cursor()

let do_bar n =
  restore_cursor();
  save_cursor();
  Printf.printf
    "[%s%s%s] %d%%%!"
    (String.make (n-1) '=')
    (if n = width then "=" else ">")
    (String.make (width-n) ' ')
    n;
 if n = width then print_newline()

let step () =
  incr state;
  let pc = (width * !state)/(!total) in
  if pc > !last_up then
    do_bar pc;
    last_up := pc

The advantage is that you just need to initialize the progress bar with the total number of steps and then call step () at each step. Nothing more. Magical!