2

Im trying to get the following output from a while loop to show the progress.

  - ########

Below is the code,

print '  - #',
x = 1
while x < 15:
    print '#',
    time.sleep(0.5)
    x += 1

But Im just getting all of the hashs printed once the while loop is complete (?)

Any ideas

felix001
  • 15,341
  • 32
  • 94
  • 121
  • Possible duplicate of [How can I print a string using a while loop with an interval in between letters (on the same line) in Python 3.2?](http://stackoverflow.com/questions/17433850/how-can-i-print-a-string-using-a-while-loop-with-an-interval-in-between-letters) – SiHa Mar 21 '16 at 13:00

5 Answers5

5
import sys
import time
while True:
    sys.stdout.write('#')
    sys.stdout.flush()
    time.sleep(0.5)
Zagfai
  • 418
  • 2
  • 12
4

There are plenty of methods to this.

  1. If you are working with Python 2.X, you need to call sys.stdout.flush() after every print.

  2. If you are using python 3 then you can get it done by doing

    print(#,sep = ' ', flush=True)

  3. You can disable the buffering completely if you run python with -u option: python -u script.py

    If you run your script directly i.e. ./script.py then specify it in the shebang line: #!/usr/bin/env python -u.

  4. The other way to do it is to set env variable PYTHONUNBUFFERED from the bash shell: export PYTHONUNBUFFERED="aa"

Adding more details to cause of the issue:

Stdout in Python is line buffered. Meaning if you had skipped the "," in your print your output will be printed as expected. But when the output is printed in the same line then it will be flushed only if there is a new line or the buffer is full. In case you redirect your output to a file instead of terminal (python script.py >output), output will be flushed only when the buffer is full. This can be verified if you pipe the output to cat: python script.py | cat

In all the methods mentioned in this answer and other answers we are explicitly telling Python to not buffer but flush the output as soon as it gets it.

More research on the topic:

This behavior is not Python Specific. This is the case with printf in C as well. It is due to glibc implementation of linux. The reasoning behind this behavior is efficiency. Because the output is buffered number of read and write operations are minimized. I found this article which gives a brief description of the same: Unix Buffering Delays

Sharad
  • 1,867
  • 14
  • 33
2

print by default writes to stdout which is stored in sys.stdout. Stdout is flushed when a new line is printed. Since you aren't printing the new line, the text isn't displayed until you do. You can, however, flush stdout yourself:

import sys
import time
print '  - #',
sys.stdout.flush()
x = 1
while x < 15:
    print '#',
    sys.stdout.flush()
    time.sleep(0.5)
    x += 1

You really don't need a while loop. A for loop would be shorter:

import sys
import time
print '  - #',
sys.stdout.flush()
x = 1
for _ in range(15):
    print '#',
    sys.stdout.flush()
    time.sleep(0.5)
zondo
  • 19,901
  • 8
  • 44
  • 83
  • You seemed interested in iterating without returning an iterator. Instead of using `_` as a variable name, you could use `for _ in itertools.repeat(None, 15):` which actually returns `None`. It might not matter here at all but it's faster and not simply an indication to the reader that the variable isn't being used. – jDo Mar 21 '16 at 14:43
  • @jDo: That's true; I hadn't thought of that. In something this small, though, I don't think the saved time is enough to warrant the extra import. I'd like to keep it simple for the OP. – zondo Mar 21 '16 at 14:46
  • yeah, just a side note. In this case it's not that important unless OP is planning on using it for writing thousands of lines and not telling us (I don't think so) – jDo Mar 21 '16 at 14:50
2

I think you already got your answer but in case you later want to overwrite the same line, add a carriage return \r before the characters you're printing (to return your typewriter's carriage to the start of the current line, of course ;) ). I use this to have my file processing scripts write to the same line - each new line overwriting the last. Some fiddling with additonal white space is necessary if the new line is shorter than the previous.

Eg. to make a rotating loader thingy that's eventually replaced by a growing line of #s:

import sys
import time

# rotating loader "animation"
# Ubuntu or the aptitude package manager uses something similar
x = 1
symbols = ["|", "/", "-", "\\"]
while x < 20:
    x+=1
    sys.stdout.write("\r" + symbols[x%len(symbols)])
    sys.stdout.flush()
    time.sleep(0.2)


# progress bar "animation" using hashtags 
x = 1
sys.stdout.write("\r")
while x < 10:
    x+=1
    sys.stdout.write('#')
    sys.stdout.flush()
    time.sleep(0.2)

print("")
jDo
  • 3,962
  • 1
  • 11
  • 30
  • Why don't you use `for _ in range(20):` and `for _ in range(10):` instead of using `while` loops? – zondo Mar 21 '16 at 13:05
  • @zondo Just because I copied it straight from an answer to the question and didn't think too much about it. Besides, I needed the iterator in this case (although there are many other ways of doing it that don't involve a lookup in `symbols`). But why don't you? – jDo Mar 21 '16 at 13:09
  • Yeah, I just realized that ;) I edited my answer just a minute ago. – zondo Mar 21 '16 at 13:12
0

Have a look at tqdm. It displays for you a good looking progress bar very easily.

from tqdm import tqdm
for i in tqdm(range(9)):
    ...
DevShark
  • 8,558
  • 9
  • 32
  • 56