Python control output positions in terminal/console - python

This is a question for python language, and the terminal/console applies to Unix-like systems. It would be great if the solution is platform independent but that is not required.
The scenario is when the program keeps printing lines to terminal. However, among the many lines to print, some of them are special, like progress bar or status indicator: they have to be placed only at the bottom of the console, while all other lines can still be printed where they normally are, one after another and the screen scrolls as normal too.
An example code solution would be much better than a theoretic one. For this purpose here is an example:
def print_status(msg):
# implement me
print msg
def print_many_lines():
print 'line one\n'
print 'line two\n'
print_status('i am here')
print 'line three\n'
print 'line four\n'
print 'line five\n'
print_status('i am changed')
print 'line six\n'
Could you help me implement the print_status function so that the msg passed to it will always be printed at the bottom of terminal?
Please note that this is very different from another similar question that, when multiple lines are printed consecutively to the terminal, how can we make sure they are printed at the same line. Using \r can be useful in that scenario but it cannot solve this problem, because (1) these special lines may more likely not be printed consecutively and (2) there will be other lines printed after these special lines but these special lines should be kept at the bottom of terminal, still.
Thanks!

For a Windows terminal try the console module For unix the curses module would do.
This is how you do it on Windows.
c = Console.getconsole()
c.text(0, -1, 'And this is the string at the bottom of the console')
By specifying -1 for the second argument, string goes at bottom line.
For Linux, a working code which prints at last line.
import time
import curses
def pbar(window):
height, width = window.getmaxyx()
for i in range(10):
window.addstr(height -1, 0, "[" + ("=" * i) + ">" + (" " * (10 - i )) + "]")
window.refresh()
time.sleep(0.5)
curses.wrapper(pbar)

It sounds like you want to use a curses library, which handles text based UIs on Unix systems. There's a module for it in python's standard library:
https://docs.python.org/2/library/curses.html#module-curses
How to use it is beyond the scope of an answer here I'm afraid.

Related

What does this mean in Python '\x1b[2K'?

I've just learnt that to clear a line that you printed in Python, do this:
sys.stdout.write('\x1b[2K')
Why is it so complicated? what does that weird code mean? and is there any alternative in print command?
Print does offer "end" option that allows to go back and forth in lines, but no way to clear what you printed. Overwriting via \r doesn't always work especially if the new line is shorter than the old one. You will get traces from the old line, so I need clearing first.
Thanks.
\x1b[2K is what's known as an ANSI terminal control sequence. They are a legacy of the 1970s and still used today (but vastly extended) to control terminal emulators.
\x1b is the ASCII for ESCAPE (literally the ESC key on your keyboard). [2K is the command "erase the current line".
There are many libraries in Python for working with the terminal, such as Urwid. These libraries will hide the inner workings of the terminal from you and give you higher-level constructs to create TUIs.
However, there is a much more efficient way of doing this:
You can use the print() command as usual, and delete the screen using
os.system("cls") # For Windows
or
os.system("clear") # For Linux
Alternative to print on a single line
I have a script that prints the x, y coordinates of the mouse as such:
import pyautogui
import time
while True:
x, y = pyautogui.position()
position_string = "X: {} Y: {}".format(str(x).rjust(4), str(y).rjust(4))
print(position_string, end='')
print('\b' * len(position_string), end='', flush=True)
time.sleep(1)
Where I will point out that you can print the backspace character ('\b') the amount of times that there are characters on the screen (len(position_string)), and when used with the end='' and flush=True options this will constantly print on a single line within your console. I should also note that this does not work in IDLE, but only on an actual command line! In IDLE the backspace characters are actually printed as some weird square shape...
This is called ANSI escape code . 2K is the name for Erase in Line. Quote from the link:
Erases part of the line. If n is 0 (or missing), clear from cursor to the end of the line. If n is 1, clear from cursor to beginning of the line. If n is 2, clear entire line. Cursor position does not change.
You can also try echo -e '\x1b[2k' in the terminal for better understanding.

Printing multiple lines of output with the CMD shell (Python)

I have a simple question today. Basically, I want to print 2 or more lines of output in the CMD shell with python. Here is an example:
Regular CMD Shell:
Output
What I want:
Output
Output1
Ect.
I'm using Python 3.x fyi. I don't even know if this is possible with typical Python libraries, thanks for your answers!
Edit: Due to confusion, I've dedcided to write more in detail. I'd like to have 2 or more live outputs while a loop or something of the matter is running. So, for example, if I was running a clock in a while True loop, I could use the carriage return function and have 2 outputs running.
Edit #2: So I'm going to give you guys a situation in which I would need an answer to this question. Basically, I made a loop that displays the current time and the time until 7:30 pm to the CMD shell using a return so it all stays neatly on one line. However, I need it so instead of me printing all the information I need on 1 line, it does it on 2. Here is the output:
Time: 12:44:38 Time Left Until 7:30: 6:45:22
What I want it to be:
Time: 12:44:38
Time Left Until 7:30: 6:45:22
Here is my code:
import datetime
import time
import sys
while True:
start_time = datetime.datetime.now().time().strftime('%H:%M:%S')
end_time = ('19:30:00')
total_time=(datetime.datetime.strptime(end_time,'%H:%M:%S') - datetime.datetime.strptime(start_time,'%H:%M:%S'))
print ('Time: ', start_time, 'Time Left Until 7:30: ', total_time, end='\r')
time.sleep(0.1)
When I try to print anything below the carriage return, it doesn't print and when I put the carriage return in the second print, the original loop prints down and not in one line. Thanks again!
If cls command blinks, you can use ANSI commands to control. First you have to enable ANSI in the windows cmd. You can use a 3rd party library such as colorama to do that.
Once you've enabled ANSI, you can use it to move up a line and delete a line (and also add nice colors 😉).
colorama.init() # don't forget to call
Up a line: sys.stout.write('\033[1A')
Delete current line: sys.stout.write(' \r\033[K')
You can refresh the cmd window by calling os.system('cls'). This will clear the screen and allow you to print your updated output.

Unprint a line on the console in Python?

Is it possible to manipulate lines of text that have already been printed to the console?
For example,
import time
for k in range(1,100):
print(str(k)+"/"+"100")
time.sleep(0.03)
#>> Clear the most recent line printed to the console
print("ready or not here I come!")
I've seen some things for using custom DOS consoles under Windows, but I would really like something that works on the command_line like does print without any additional canvases.
Does this exist? If it doesn’t, why not?
P.S.: I was trying to use curses, and it was causing problems with my command line behaviour outside of Python. (After erroring out of a Python script with curses in it, my Bash shell stopped printing newline -unacceptable- ).
What you're looking for is:
print("{}/100".format(k), "\r", end="")
\r is carriage return, which returns the cursor to the beginning of the line. In effect, whatever is printed will overwrite the previous printed text. end="" is to prevent \n after printing (to stay on the same line).
A simpler form as suggested by sonrad10 in the comments:
print("{}/100".format(k), end="\r")
Here, we're simply replacing the end character with \r instead of \n.
In Python 2, the same can be achieved with:
print "{}/100".format(k), "\r",
What you need are ANSI Command Codes.
http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes
You also need code to activate ANSI Command Codes. I would use Colorama.
https://pypi.python.org/pypi/colorama
OR
Use curses (Python 3.4+) module.
The simplest method (at least for Python 2.7) is to use the syntax:
print 'message', '\r',
print 'this new message now covers the previous'
Notice the extra ',' at the end of the first print. This makes print stay on the same line. Meanwhile, the '\r' puts the print at the beginning of that line. So the second print statement overwrites the first.

'\b' doesn't print backspace in PyCharm console

I am trying to update the last line in PyCharm's console. Say, I print a and then I want to change it to c. However, I encounter the following problem. When I run:
print 'a\bc'
it prints
a c
while the desired output (which is also what I see in the Windows console) is:
c
Is there a way to move the cursor back in PyCharm's console? or maybe delete the whole line?
This is not a bug, this is a limitation of the interactive console found both in PyCharm, and in the IDLE shell.
When using the command prompt of windows, or a linux shell - the \b character is interpreted as a backspace and implemented as it is being parsed - However, in the interactive console of PyCharm and IDLE the \b character and many others are disabled, and instead you simply get the ASCII representation of the character (a white space in most cases).
It's a known bug: http://youtrack.jetbrains.com/issue/PY-11300
If you care about this, please get an account on the bug tracker and upload the bug to give it more attention.
The \r works. I know this is ASCII Carriage Return, but i use this as a workaround
print("\ra")
print("\rc")
will yield in c in the console
By the way, backspace is a ASCII Character
I just ran into the same issue in PyCharm (2019.1) and stumbled on this post. It turns out that you can use the \b character if you use the sys.stdout.write function instead of print. I wasn't able to get any of the above examples working within PyCharm using the print function.
Here's how I update the last line of text in my code assuming I don't need more than 100 characters:
# Initialize output line with spaces
sys.stdout.write(' ' * 100)
# Update line in a loop
for k in range(10)
# Generate new line of text
cur_line = 'foo %i' % k
# Remove last 100 characters, write new line and pad with spaces
sys.stdout.write('\b' * 100)
sys.stdout.write(cur_line + ' '*(100 - len(cur_line)))
# ... do other stuff in loop
This should generate "foo 1", then replaced with "foo 2", "foo 3", etc. all on the same line and overwriting the previous output for each string output. I'm using spaces to pad everything because different programs implement the backspace character differently, where sometimes it removes the character, and other times it only moves the cursor backwards and thus still requires new text to overwrite.
I've got to credit the Keras library for this solution, which correctly updates the console output (including PyCharm) during learning. I found that they were using the sys.stdout.write function in their progress bar update code.

Python print statement prints nothing with a carriage return

I'm trying to write a simple tool that reads files from disc, does some image processing, and returns the result of the algorithm. Since the program can sometimes take awhile, I like to have a progress bar so I know where it is in the program. And since I don't like to clutter up my command line and I'm on a Unix platform, I wanted to use the '\r' character to print the progress bar on only one line.
But when I have this code here, it prints nothing.
# Files is a list with the filenames
for i, f in enumerate(files):
print '\r%d / %d' % (i, len(files)),
# Code that takes a long time
I have also tried:
print '\r', i, '/', len(files),
Now just to make sure this worked in python, I tried this:
heartbeat = 1
while True:
print '\rHello, world', heartbeat,
heartbeat += 1
This code works perfectly. What's going on? My understanding of carriage returns on Linux was that it would just move the line feed character to the beginning and then I could overwrite old text that was written previously, as long as I don't print a newline anywhere. This doesn't seem to be happening though.
Also, is there a better way to display a progress bar in a command line than what I'm current trying to do?
Try adding sys.stdout.flush() after the print statement. It's possible that print isn't flushing the output until it writes a newline, which doesn't happen here.
Handling of carriage returns in Linux differs greatly between terminal-emulators.
Normally, one would use terminal escape codes that would tell the terminal emulator to move the virtual "carriage" around the screen (think full-screen programs running over BBS lines). The ones I'm aware of are the VT100 escape codes:
\e[A: up
\e[B: down
\e[C: right
\e[D: left
\e[1~: home
\e[4~: end
Where \e is the escape character, \x1b.
Try replacing all \r's with \e[1~
Also see this post
If your terminal is line-buffered, you may need a sys.stdout.flush() to see your printing if you don't issue a linefeed.

Categories