Skip to content

Instantly share code, notes, and snippets.

@rougier
Created January 25, 2016 06:25
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rougier/c0d31f5cbdaac27b876c to your computer and use it in GitHub Desktop.
Save rougier/c0d31f5cbdaac27b876c to your computer and use it in GitHub Desktop.
A progress bar using unicode character for smoother display
# -----------------------------------------------------------------------------
# Copyright (c) 2016, Nicolas P. Rougier
# Distributed under the (new) BSD License.
# -----------------------------------------------------------------------------
import sys, math
def progress(value, length=40, title = " ", vmin=0.0, vmax=1.0):
"""
Text progress bar
Parameters
----------
value : float
Current value to be displayed as progress
vmin : float
Minimum value
vmax : float
Maximum value
length: int
Bar length (in character)
title: string
Text to be prepend to the bar
"""
# Block progression is 1/8
blocks = ["", "▏","▎","▍","▌","▋","▊","▉","█"]
vmin = vmin or 0.0
vmax = vmax or 1.0
lsep, rsep = "▏", "▕"
# Normalize value
value = min(max(value, vmin), vmax)
value = (value-vmin)/float(vmax-vmin)
v = value*length
x = math.floor(v) # integer part
y = v - x # fractional part
base = 0.125 # 0.125 = 1/8
prec = 3
i = int(round(base*math.floor(float(y)/base),prec)/base)
bar = "█"*x + blocks[i]
n = length-len(bar)
bar = lsep + bar + " "*n + rsep
sys.stdout.write("\r" + title + bar + " %.1f%%" % (value*100))
sys.stdout.flush()
# -----------------------------------------------------------------------------
if __name__ == '__main__':
import time
for i in range(1000):
progress(i, vmin=0, vmax=999)
time.sleep(0.0025)
sys.stdout.write("\n")
@marc1n
Copy link

marc1n commented Sep 29, 2017

Cool!
Instead of:

  base = 0.125
  prec = 3
  i = int(round(base*math.floor(float(y)/base),prec)/base)

you can write a simpler expression:

i = int(round(y*8))

@sebassdc
Copy link

This is awesome! I've made myself a port to Javascript (es6) with a little add (progresive option):

//@ts-check

const progress = ({
  value,
  length=40,
  title = " ",
  vmin=0.0,
  vmax=1.0,
  progressive = false
}) => {
  // Block progression is 1/8
  const blocks = ["", "▏","▎","▍","▌","▋","▊","▉","█"]
  const lsep = "▏", rsep = "▕"
  
  // Normalize value
  const normalized_value = (Math.min(Math.max(value, vmin), vmax)-vmin)/Number(vmax-vmin)
  const v = normalized_value * length
  const x = Math.floor(v) // integer part
  const y = v - x         // fractional part
  const i = Math.round(y*8)
  const bar = Array(x).fill("█").join("") + blocks[i]
  const remaining = Array(length - bar.length).fill(" ").join("")
  return `${lsep}${bar}${!progressive ? remaining : ""}${rsep} ${(Math.round(normalized_value * 100 * 100) / 100)}%`
}

let prevStr = ""
for (let i = 0; i < 1000; i++) {
  prevStr = Array(prevStr.length).fill('\b').join('') + progress({value: i, vmin: 0, vmax: 999})
  process.stderr.write(prevStr)
}
prevStr = ""
for (let i = 0; i < 1000; i++) {
  prevStr = Array(prevStr.length).fill('\b').join('') + progress({value: i, vmin: 0, vmax: 999, progressive: true})
  process.stderr.write(prevStr)
}

@WinEunuuchs2Unix
Copy link

WinEunuuchs2Unix commented Dec 31, 2020

I wonder if this chokes on Python 2.7.12 or if I copied script wrong? I get this error:

Traceback (most recent call last):
  File "./progress_bar2", line 54, in <module>
    progress(i, vmin=0, vmax=999)
  File "./progress_bar2", line 41, in progress
    bar = "█"*x + blocks[i]
TypeError: can't multiply sequence by non-int of type 'float'

I did add two lines to the top of the script though:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

Any thoughts?

@rougier
Copy link
Author

rougier commented Dec 31, 2020

int(x) instead of x maybe.

@WinEunuuchs2Unix
Copy link

WinEunuuchs2Unix commented Jan 1, 2021

@Rougler Thanks for the quick reply. I've actually went with a bash solution to the progress bar.

progress_bar3

The code is here

@HeyBanditoz
Copy link

HeyBanditoz commented Dec 26, 2022

Here it is in Java, not really tested too much but seems to work okay.

import static java.lang.Math.*;

public class ProgressBar {
    private static final String[] blocks = new String[]{"", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"};
    private static final double base = 0.125;

    /**
     * Renders a unicode block-based progress bar.
     *
     * @param value  Current value to be displayed as progress.
     * @param vmin   Minimum value, usually 0.0.
     * @param vmax   Maximum value, usually 1.0.
     * @param length Length of the progress bar, in characters.
     * @return A rendered progress bar.
     * @author <a href="https://gist.github.com/rougier/c0d31f5cbdaac27b876c">rougier</a>
     */
    public static String generateProgressBar(double value, double vmin, double vmax, int length) {
        // normalize value
        value = min(max(value, vmin), vmax);
        value = (value - vmin) / (vmax - vmin);

        double v = value * length;
        int x = (int) floor(v); // integer part
        double y = v - x;       // fractional part
        // round(base*math.floor(float(y)/base),prec)/base
        int i = (int) ((round((base * floor(y / base)) * 1000D) / base) / 1000D);
        String bar = "█".repeat(x) + blocks[i];
        int n = length - bar.length();
        return bar + " ".repeat(n);
    }
}

@WinEunuuchs2Unix
Copy link

WinEunuuchs2Unix commented Apr 4, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment