Post Image

Python Overwrite Output Line

If you are familiar with command line applications you have likely seen where there is a single line of output that is updated as progress is made or as events happen so the screen doesn't fill up with unnecessary data, a perfect example is a percentage counter that will show the progress of a long running task. Overwriting a line of output in a command line application in Python is a trivial task by just specifying a different ending in the print statement but with some additional technique is very versatile.

 

 

Base Code

Below I have a base script we will use to build off of.

from time import sleep

if __name__ == '__main__':

    for x in range(0, 101):
        print(f'Percent Complete: {x}%')
        sleep(.02)

This is just a simple for loop to simulate a long running task by sleeping .02 seconds every loop and printing out the percentage complete.

 

Now if you run this code you can see if fills up the console with a bunch of lines with each percentage. In my opinion this is a bad design from the users perspective because they get a bunch of data that means nothing almost instantly so lets instead maintain a single line and print the percentage.

 

 

How to Overwrite a Line After Printing.

The secret sauce here is the built in print() statement accepts an additional argument end which specifies the line ending to put after whatever your printing. By default it is \n which is a new line, this is why each print statement will be on its own line. To instead bring the cursor to the beginning of the line we can specify the end to be \r which will bring the cursor back to the beginning of the line and anything printed after will overwrite what was previously there. Below is the modified code from our base.

from time import sleep

if __name__ == '__main__':

    for x in range(0, 101):
        print(f'Percent Complete: {x}%', end='\r')
        sleep(.02)

As you can see the only modification is on my print statement where I specify end='\r'. Unfortunately I can't demonstrate what running this code will look like on a static page, but copy and paste it to your computer and run it yourself, you will see there will be a single line of output and it will increment to 100% and return a prompt leaving your output something like below after completion.

$ python myscript.py
Percent Complete: 100%
$

And from a basic perspective that is all you need to do, however I have seen instances where the console will overwrite the output with the prompt which is another easy fix, after your long running task just add an empty print statement, an excerpt of what that looks like from our base is below.

for x in range(0, 101):
    print(f'Percent Complete: {x}%', end='\r')
    sleep(.02)
print()

 

 

Limitation of that Solution

In the above example it is very easy to handle that because no matter what the output will longer than the last output thus not leaving any old text laying around but lets take the example below.

if __name__ == '__main__':

    print('This is my first line of output', end='\r')
    print('Second line', end='\r')

Now if you copy this and run it you will see the following output on your terminal.

$ python myscript.py
Second linefirst line of output
$

So you can see that there is still text left over from the first print line because it was longer than the second, so how can we solve this, we could modify our send print statement to have a bunch of whitespace after it like below.

print('Second line' + ' ' * 20, end='\r')

Which does give our intended result.

$ python myscript.py
Second line
$

But this is not a good way of doing it because we could overshoot the width of our terminal causing a line wrap, or undershoot the previous line of text.

 

Another idea would be having some sort of tracking variable and/or extended print function that keeps track of the max length of output you have printed and always adds enough whitespace to cover that. While again this will work there is a more elegant way of handling it.

 

 

Get Width of Console and Overwrite Everything

Python has a way of getting the width of the console in its current window configuration so you know exactly how many characters can fit on a single line at any given time, and if a user resizes the window the next time you check it will change based on the size of the window. Below I have an example script that will print out the terminal size, copy it down and run it, resize the window to a random size and run it again you can see the output change.

from os import get_terminal_size

if __name__ == '__main__':
    columns, rows = get_terminal_size()
    print(f'Rows: {rows}, Columns: {columns}')

Now that we know how to do this we can build a function to clear an entire line of output from the command line which is below.

def clear_terminal_line(end='\r'):
    columns, _ = get_terminal_size()
    print('', end='\r')  # return cursor to beginning
    print(' ' * (columns - 1), end=end)  # Fill line with spaces

Stepping through this function:

    1. Get the maximum columns
    2. Return the cursor to the beginning with an empty print statement and an ending character of \r
    3. Print a number of spaces equal to the number of columns in the terminal window

In addition we can specify the return character of the clearing print statement which defaults to \r to increase flexibility.

 

Wrapping this function into our initial example we have the code below.

from os import get_terminal_size
from time import sleep


def clear_terminal_line(end='\r'):
    columns, _ = get_terminal_size()
    print('', end='\r')  # return cursor to beginning
    print(' ' * (columns - 1), end=end)  # Fill line with spaces


if __name__ == '__main__':

    for x in range(0, 101):
        clear_terminal_line()
        print(f'Percent Complete: {x}%', end='\r')
        sleep(.02)

Stepping through our for loop the first thing we do is clear the line, print our update, and sleep to simulate a task running. Just like the initial example you will get the same output but this is a fundamentally more flexible way of clearing out the previous line of output and printing a continuous update.

 

If you still have questions or concerns feel free to leave a comment so I can help clear up anything that I missed!



Comments (0)
Leave a Comment