2

I'm trying to make a kind of simple progress bar, when each half a second the percentage is going up; The output is constantly updating itself. For this I tried to use the '\r' argument, but the code just ignores it and prints the progress bar over and over until it gets to 100 percent.

Here is the code:

import time
def progressBar(value, endvalue, bar_length=20):
    while value <= endvalue:
        percent = float(value) / endvalue
        arrow = '-' * int(round(percent * bar_length)-1) + '>'
        spaces = ' ' * (bar_length - len(arrow))
        print("\rPercent: [{0}] {1}%".format(arrow + spaces, int(round(percent * 100))))
        value+=1
        time.sleep(0.5)

progressBar(1, 100)

output:

Percent: [>                   ] 1%

Percent: [>                   ] 2%

Percent: [>                   ] 3%

Percent: [>                   ] 4%

Percent: [>                   ] 5%

Percent: [>                   ] 6%

Percent: [>                   ] 7%

Percent: [->                  ] 8%

Percent: [->                  ] 9%

Percent: [->                  ] 10%

Percent: [->                  ] 11%

And so on and on.

Can someone tell me what is the problem here?

Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
Daniel Engel
  • 158
  • 3
  • 13

3 Answers3

1

You need to tell print() not to print a newline:

print("\rPercent: [{0}] {1}%".format(arrow + spaces, int(round(percent * 100))),
      end="")

Setting end to an empty string replaces the default \n written.

You probably want to add a print() after the while loop is complete to add an explicit newline.

Demo with just end="":

GIF video of progress bar

You may want to add flush=True to ensure that there isn't a buffer problem (some terminal buffers wait for a newline before flushing):

print("\rPercent: [{0}] {1}%".format(arrow + spaces, int(round(percent * 100))),
      end="", flush=True)

Alternatively, use sys.stdout.write() and sys.stdout.flush():

sys.stdout.write("\rPercent: [{0}] {1}%".format(arrow + spaces, int(round(percent * 100))))
sys.stdout.flush()

but print produces the exact same calls to sys.stdout.write() anyway.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • it gives me this: ` Percent: [> ] 1% Percent: [> ] 2% Percent: [> ] 3% Percent: [> ] 4% Percent: [> ] 5% Percent: [> ] 6% Percent: [> ] 7% Percent: [-> ] 8% Percent: [-> ] 9% Percent: [-> ] 10% Percent: [-> ] 11% Percent: [-> ] 12% Percent: [--> ] 13% Percent: [--> ] 14% Percent: [--> ` – Daniel Engel Aug 16 '16 at 09:08
  • it is ignoring my '\r' somehow – Daniel Engel Aug 16 '16 at 09:09
  • @DanielEngel: what console are you using? `\r` needs to be supported by the console or terminal. It won't work in many IDE's for example. – Martijn Pieters Aug 16 '16 at 09:11
  • In terminal it doesn't even open for me – Daniel Engel Aug 16 '16 at 09:15
  • @DanielEngel: that doesn't tell me anything; I've added a demo video of the code pasted into an interactive Python 3.5 session. – Martijn Pieters Aug 16 '16 at 09:21
  • when I write end="" it complains with an error: invalid syntax (only on console) – Daniel Engel Aug 16 '16 at 09:25
  • @DanielEngel: did you include the comma after the other argument to the `print()` function? Or are you using Python 2 (where `print` is a *statement* not a function)? – Martijn Pieters Aug 16 '16 at 09:27
  • 1
    @DanielEngel: if you are using Python 2, I recommend you use `from __future__ import print_function` at the top of your script. Alternatively, remove the `()` parentheses and use `print "...".format(...),` (adding a comma after the string you are printing) or use `import sys` then `sys.stdout.write("...".format(...))` to write directly to stdout without a newline. – Martijn Pieters Aug 16 '16 at 09:28
  • I am using python 3 – Daniel Engel Aug 16 '16 at 09:31
  • @DanielEngel: then either you didn't copy my altered `print()` function exactly, or you *also* have Python 2 installed and accidentally ran that in the terminal. Hard to tell without seeing your exact code and output. – Martijn Pieters Aug 16 '16 at 09:32
  • my code is the same as yours – Daniel Engel Aug 16 '16 at 09:33
  • Yet mine doesn't have a syntax error. We are going in circles here. – Martijn Pieters Aug 16 '16 at 09:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/121042/discussion-between-daniel-engel-and-martijn-pieters). – Daniel Engel Aug 16 '16 at 09:36
0

Existing progressbar-implementations (also in other applications, MATLAB, e.g.) go to great lengths to track the number of characters they send to the output stream at each update, so that they can send as many backspace-characters \b needed to "erase" those characters. (And it depends on your platform/console and how your script is called.)

The \r doesn't work like you'd expect in the output-stream of a console.

Pierre Schroeder
  • 672
  • 1
  • 5
  • 14
  • 1
    `\r` works just fine in *most* terminals. Not in all consoles (IDE output windows) however. – Martijn Pieters Aug 16 '16 at 09:12
  • so what other way should I use that is not confusing as hell? – Daniel Engel Aug 16 '16 at 09:12
  • @MartijnPieters you are right of course (although I'm not sure if we can make a reliable quantitative statement about the ratio between terminals where it works or not). Considering the number of terminals which don't support \r as wanted, it's safer to expect that it doesn't when writing a piece of code. And even then, running a script through batch on Windows 7 for example, can't handle "backspacing" past a multi-line progressbar correctly. – Pierre Schroeder Aug 16 '16 at 09:22
  • @PierreSchroeder: it's good enough for Mercurial; the [progress bar code uses `\r`](https://selenic.com/hg/file/tip/mercurial/progress.py#l161). – Martijn Pieters Aug 16 '16 at 09:51
0

I have installed python 2.7.10 in OSX 10.11.6, and the following code may meet your requirement in the Terminal

import time
import sys
def progressBar(value, endvalue, bar_length=20):
    while value <= endvalue:
        percent = float(value) / endvalue
        arrow = '-' * int(round(percent * bar_length)-1) + '>'
        spaces = ' ' * (bar_length - len(arrow))
        print("\rPercent: [{0}] {1}%".format(arrow + spaces, int(round(percent * 100))))
        sys.stdout.write("\033[F")
        value+=1
        time.sleep(0.5)

progressBar(1, 100)
clevertension
  • 6,929
  • 3
  • 28
  • 33