Rewrite multiple lines in the console
Question:
I know it is possible to consistently rewrite the last line displayed in the terminal with “r”, but I am having trouble figuring out if there is a way to go back and edit previous lines printed in the console.
What I would like to do is reprint multiple lines for a text-based RPG, however, a friend was also wondering about this for an application which had one line dedicated to a progress bar, and another describing the download.
i.e. the console would print:
Moving file: NameOfFile.txt
Total Progress: [######## ] 40%
and then update appropriately (to both lines) as the program was running.
Answers:
On Unix, use the curses module.
On Windows, there are several options:
- PDCurses: http://www.lfd.uci.edu/~gohlke/pythonlibs/
- The HOWTO linked above recommends the Console module
- http://newcenturycomputers.net/projects/wconio.html
- http://docs.activestate.com/activepython/2.6/pywin32/win32console.html
Simple example using curses (I am a total curses n00b):
import curses
import time
def report_progress(filename, progress):
"""progress: 0-10"""
stdscr.addstr(0, 0, "Moving file: {0}".format(filename))
stdscr.addstr(1, 0, "Total progress: [{1:10}] {0}%".format(progress * 10, "#" * progress))
stdscr.refresh()
if __name__ == "__main__":
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
try:
for i in range(10):
report_progress("file_{0}.txt".format(i), i+1)
time.sleep(0.5)
finally:
curses.echo()
curses.nocbreak()
curses.endwin()
Ultimately, if you want to manipulate the screen, you need to use the underlying OS libraries, which will typically be:
- curses (or the underlying terminal control codes as tracked by the terminfo/termcap database) on Linux or OSX
- the win32 console API on Windows.
The answer from @codeape already gives you some of the many options if you don’t mind sticking to one OS or are happy to install third party libraries on Windows.
However, if you want a cross-platform solution that you can simply pip install, you could use asciimatics. As part of developing this package, I’ve had to resolve the differences between environments to provide a single API that works on Linux, OSX and Windows.
For progress bars, you could use the BarChart object as shown in this demo using this code.
Here is a Python module for both Python 2/3, which can simply solve such situation with a few line of code ;D
reprint – A simple module for Python 2/3 to print and refresh multi line output contents in terminal
You can simply treat that output
instance as a normal dict
or list
(depend on which mode you use). When you modify that content in the output
instance, the output in terminal will automatically refresh 😀
For your need, here is the code:
from reprint import output
import time
if __name__ == "__main__":
with output(output_type='dict') as output_lines:
for i in range(10):
output_lines['Moving file'] = "File_{}".format(i)
for progress in range(100):
output_lines['Total Progress'] = "[{done}{padding}] {percent}%".format(
done = "#" * int(progress/10),
padding = " " * (10 - int(progress/10)),
percent = progress
)
time.sleep(0.05)
Like this:
#!/usr/bin/env python
import sys
import time
from collections import deque
queue = deque([], 3)
for t in range(20):
time.sleep(0.5)
s = "update %d" % t
for _ in range(len(queue)):
sys.stdout.write("x1b[1Ax1b[2K") # move up cursor and delete whole line
queue.append(s)
for i in range(len(queue)):
sys.stdout.write(queue[i] + "n") # reprint the lines
I discovered this in the Jiri project, written in Go.
Even better: erase all lines after done:
#!/usr/bin/env python
import sys
import time
from collections import deque
queue = deque([], 3)
t = 0
while True:
time.sleep(0.5)
if t <= 20:
s = "update %d" % t
t += 1
else:
s = None
for _ in range(len(queue)):
sys.stdout.write("x1b[1Ax1b[2K") # move up cursor and delete whole line
if s != None:
queue.append(s)
else:
queue.popleft()
if len(queue) == 0:
break
for i in range(len(queue)):
sys.stdout.write(queue[i] + "n") # reprint the lines
I found simple solution with a “magic_char”.
magic_char = ' 33[F'
multi_line = 'FirstnSecondnThird'
ret_depth = magic_char * multi_line.count('n')
print('{}{}'.format(ret_depth, multi_line), end='', flush = True)
Carriage return can be used to go to the beginning of line, and ANSI code ESC A
("
I know it is possible to consistently rewrite the last line displayed in the terminal with “r”, but I am having trouble figuring out if there is a way to go back and edit previous lines printed in the console.
What I would like to do is reprint multiple lines for a text-based RPG, however, a friend was also wondering about this for an application which had one line dedicated to a progress bar, and another describing the download.
i.e. the console would print:
Moving file: NameOfFile.txt
Total Progress: [######## ] 40%
and then update appropriately (to both lines) as the program was running.
On Unix, use the curses module.
On Windows, there are several options:
- PDCurses: http://www.lfd.uci.edu/~gohlke/pythonlibs/
- The HOWTO linked above recommends the Console module
- http://newcenturycomputers.net/projects/wconio.html
- http://docs.activestate.com/activepython/2.6/pywin32/win32console.html
Simple example using curses (I am a total curses n00b):
import curses
import time
def report_progress(filename, progress):
"""progress: 0-10"""
stdscr.addstr(0, 0, "Moving file: {0}".format(filename))
stdscr.addstr(1, 0, "Total progress: [{1:10}] {0}%".format(progress * 10, "#" * progress))
stdscr.refresh()
if __name__ == "__main__":
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
try:
for i in range(10):
report_progress("file_{0}.txt".format(i), i+1)
time.sleep(0.5)
finally:
curses.echo()
curses.nocbreak()
curses.endwin()
Ultimately, if you want to manipulate the screen, you need to use the underlying OS libraries, which will typically be:
- curses (or the underlying terminal control codes as tracked by the terminfo/termcap database) on Linux or OSX
- the win32 console API on Windows.
The answer from @codeape already gives you some of the many options if you don’t mind sticking to one OS or are happy to install third party libraries on Windows.
However, if you want a cross-platform solution that you can simply pip install, you could use asciimatics. As part of developing this package, I’ve had to resolve the differences between environments to provide a single API that works on Linux, OSX and Windows.
For progress bars, you could use the BarChart object as shown in this demo using this code.
Here is a Python module for both Python 2/3, which can simply solve such situation with a few line of code ;D
reprint – A simple module for Python 2/3 to print and refresh multi line output contents in terminal
You can simply treat that output
instance as a normal dict
or list
(depend on which mode you use). When you modify that content in the output
instance, the output in terminal will automatically refresh 😀
For your need, here is the code:
from reprint import output
import time
if __name__ == "__main__":
with output(output_type='dict') as output_lines:
for i in range(10):
output_lines['Moving file'] = "File_{}".format(i)
for progress in range(100):
output_lines['Total Progress'] = "[{done}{padding}] {percent}%".format(
done = "#" * int(progress/10),
padding = " " * (10 - int(progress/10)),
percent = progress
)
time.sleep(0.05)
Like this:
#!/usr/bin/env python
import sys
import time
from collections import deque
queue = deque([], 3)
for t in range(20):
time.sleep(0.5)
s = "update %d" % t
for _ in range(len(queue)):
sys.stdout.write("x1b[1Ax1b[2K") # move up cursor and delete whole line
queue.append(s)
for i in range(len(queue)):
sys.stdout.write(queue[i] + "n") # reprint the lines
I discovered this in the Jiri project, written in Go.
Even better: erase all lines after done:
#!/usr/bin/env python
import sys
import time
from collections import deque
queue = deque([], 3)
t = 0
while True:
time.sleep(0.5)
if t <= 20:
s = "update %d" % t
t += 1
else:
s = None
for _ in range(len(queue)):
sys.stdout.write("x1b[1Ax1b[2K") # move up cursor and delete whole line
if s != None:
queue.append(s)
else:
queue.popleft()
if len(queue) == 0:
break
for i in range(len(queue)):
sys.stdout.write(queue[i] + "n") # reprint the lines
I found simple solution with a “magic_char”.
magic_char = ' 33[F'
multi_line = 'FirstnSecondnThird'
ret_depth = magic_char * multi_line.count('n')
print('{}{}'.format(ret_depth, multi_line), end='', flush = True)
Carriage return can be used to go to the beginning of line, and ANSI code ESC A
("