2
\$\begingroup\$

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 --------
2022/18/06 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.

\$\endgroup\$
4
  • \$\begingroup\$ ERASE_LINE = '\x1b[2K'; sys.stdout.write(ERASE_LINE) see ANSI Escape Sequences. \$\endgroup\$
    – JosefZ
    Jun 20 at 14:17
  • \$\begingroup\$ Hi @JosefZ this method generate a multiple blank space before new print: . -------- test -------- (I had to add a dot . at the beginning because the formatting ignores whitespace at the beginning) \$\endgroup\$ Jun 20 at 14:22
  • \$\begingroup\$ sys.stdout.write('\r' + ERASE_LINE) or sys.stdout.write(ERASE_LINE + '\r'). There are no spaces at the erased line however the cursor position does not change by ERASE_LINE itself. \$\endgroup\$
    – JosefZ
    Jun 20 at 14:41
  • \$\begingroup\$ Perfect, I understood the process indicated by you, thank's @JosefZ ! \$\endgroup\$ Jun 20 at 14:46

1 Answer 1

1
\$\begingroup\$

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()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.