The method to overwrite the same line is well known and I use it for countdown, but what about when the next output string has a different size and will be generated by a single print? That's the reason for my question!
I use a text countdown timer like (all on the same line):
Next activation in 5 seconds
Next activation in 4 seconds
Next activation in 3 seconds
Next activation in 2 seconds
Next activation in 1 seconds
In order not to waste unnecessary lines, I created a false way to produce the same result as the keyboard backspace button (false because it actually only writes over the top with blank spaces, giving the false impression that we return to the first position left writing in the terminal):
import sys
import time
from datetime import datetime
def main():
while True:
print('-------- test --------')
print(datetime.now().strftime('%Y/%d/%m %H:%M'))
for remaining in range(5, 0, -1):
sys.stdout.write('\r')
txt_value = f'Next activation in {remaining:2d} seconds'
sys.stdout.write(txt_value)
sys.stdout.flush()
time.sleep(1)
sys.stdout.write('\r' + len(txt_value)*' ')
sys.stdout.write('\r')
if __name__ == '__main__':
main()
Output countdown:
-------- test --------
Next activation in 5 seconds
Output after full countdown:
-------- test --------
20221806 21:21
It's a pretty archaic method (at least I imagine it is), so I brought this question for improvements and professionalization of the method.
1 Answer 1
ANSI escape sequences are not necessary, and the so-called archaic method of using a carriage return is preferred.
Avoid mixing sys.stdout
with print
; prefer the latter. This offers a flush
kwarg to be used instead of sys.stdout.flush()
.
For the purposes of demonstration it's best if you include a seconds-field in your timestamp.
Think about the assumptions of print()
. It has a default ending character of a linefeed. A more uniform approach to printing uses either a terminating linefeed or a terminating carriage return, not a prefix carriage return. During the sleeping period, the carriage return will have the cursor sitting at the beginning of the line, ready to overwrite with either another progress banner or real output.
Suggested
from time import sleep
from datetime import datetime
def main():
progress_len = 0
while True:
print(
f'{"-------- test --------":{progress_len}}\n'
f'{datetime.now():%Y/%d/%m %H:%M:%S}'
)
for remaining in range(5, 0, -1):
txt_value = f'Next activation in {remaining:2d} seconds'
progress_len = len(txt_value)
print(txt_value, end='\r', flush=True)
sleep(1)
if __name__ == '__main__':
main()
ERASE_LINE = '\x1b[2K'; sys.stdout.write(ERASE_LINE)
see ANSI Escape Sequences. \$\endgroup\$. -------- test --------
(I had to add a dot.
at the beginning because the formatting ignores whitespace at the beginning) \$\endgroup\$sys.stdout.write('\r' + ERASE_LINE)
orsys.stdout.write(ERASE_LINE + '\r')
. There are no spaces at the erased line however the cursor position does not change byERASE_LINE
itself. \$\endgroup\$