Erich Purpur
Erich Purpur

Reputation: 1529

sys.stdout.write \r carriage, how to overwrite all characters?

I'm playing with itertools.cycle and I'm using a simple list as input. Then I write a while loop and I want to basically overwrite my output with each color as I iterate through them. The sys.stdout.write('\r' + colors) line does not overwrite all characters, only the length of the string of the next color. Lastly, I have a .5 second delay between each iteration.

import itertools
import time
colors = ['green', 'yellow', 'red']
traffic_light = itertools.cycle(colors)
while True:
    sys.stdout.write('\r' + next(traffic_light))
    sys.stdout.flush()
    time.sleep(.5)

When I get to 'yellow' in my loop, I am left with 'w' or 'low' when the shorter 'green' and 'red' strings are printed. My output looks like this (after the first loop when 'yellow' is printed).

redlow
greenw
yellow

Can I completely overwrite the output with the '\r' carriage?

Upvotes: 1

Views: 1777

Answers (3)

Dietrich Epp
Dietrich Epp

Reputation: 213807

The carriage return '\r' will send the cursor to the beginning of the line, where it can overwrite the existing text. You can combine this with the sequence CSI K, which erases from the current cursor to the end of the line.

Just replace \r with \r\x1b[K. See ANSI escape code.

import itertools
import sys
import time
colors = ['green', 'yellow', 'red']
traffic_light = itertools.cycle(colors)
while True:
    sys.stdout.write('\r\x1b[K' + next(traffic_light))
    sys.stdout.flush()
    time.sleep(.5)

Try out these additional escape sequences:

# Add color
colors = ['\x1b[32mgreen', '\x1b[33myellow', '\x1b[31mred']

Note the limitations of this technique... if the terminal is short enough that your text wraps, the program will move forward one line every time you print. If you need something more robust, curses gives you more power but it doesn't work out of the box on Windows.

Upvotes: 4

blhsing
blhsing

Reputation: 107095

You can calculate the maximum width of the color strings and then use str.ljust to pad the output with enough spaces to fill to the maximum width:

import itertools
import time
import sys
colors = ['green', 'yellow', 'red']
traffic_light = itertools.cycle(colors)
max_width = max(map(len, colors))
while True:
    sys.stdout.write('\r' + next(traffic_light).ljust(max_width))
    sys.stdout.flush()
    time.sleep(.5)

Upvotes: 3

Steven Rumbalski
Steven Rumbalski

Reputation: 45562

Create a format string that left justifies to the max width.

import itertools
import time

colors = ['green', 'yellow', 'red']
fmt = f'\r{{:<{max(map(len, colors))}}}' # fmt = '{:<7}'

for color in itertools.cycle(colors):
    print(fmt.format(color), end='') # if needed add: flush=True
    time.sleep(.5)

Prior to 3.6 use fmt = '\r{{:<{}}}'.format(max(map(len, colors))).

Alternatively use the .ljust() string method:

import itertools
import time

colors = ['green', 'yellow', 'red']
width = max(map(len, colors))

for color in itertools.cycle(colors):
    print('\r' + color.ljust(width), end='') # if needed add: flush=True
    time.sleep(.5)

Upvotes: 0

Related Questions