How to edit console output - python

I have the following very simple code
stdout.write("Hello World")
stdout.write("\rBye world")
stdout.write("\rActually hello back")
Which prints as expected 'Actually hello back' however if i were to add a newline
stdout.write("\n")
How can I go back a newline and then to the beginning of the line so I can actually just output "Hi" instead of
Actually hello back
Hi
I tried
stdout.write("\r")
stdout.write("\b")
However none of them seem to do the trick. The end goal is to display a big chunk of text and then update the output in real time without having to write again. How can I achieve this in python?
EDIT
My question is different than the one suggested as I don't want to modify a single line. I want to be able to print 4-5 lines of text and then replace them in real time instead of just one line that is modified.

Well, if You want to gain full control over the terminal, I would suggest to use the curses library.
The curses module provides an interface to the curses library, the
de-facto standard for portable advanced terminal handling.
Using it, You can edit multiple lines in terminal like this:
import curses
import time
stdscr = curses.initscr()
stdscr.addstr("line 1\n")
stdscr.addstr("line 2\n")
stdscr.refresh()
time.sleep(3)
stdscr.erase()
stdscr.addstr("edited line 1\n")
stdscr.addstr("edited line 2\n")
stdscr.refresh()
time.sleep(3)
curses.endwin()
The capabilities of this library are much greater though. Full tutorial here.

Related

Display colored ascii art in terminal using Python curses

I want to display a colored ascii art in a terminal (for e.g. windows cmd) using python-curses. I wrote something really basic like this :
import time
import curses
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
f = open('betterdays.ansi.txt',"r",encoding="utf8")
asciiart = f.read()
for y, line in enumerate(asciiart.splitlines(), 2):
stdscr.addstr(y, 2, line)
stdscr.refresh()
time.sleep(5)
curses.echo()
curses.nocbreak()
stdscr.keypad(False)
curses.endwin()
whihc gives me the error:
stdscr.addstr(y, 2, line)
_curses.error: addwstr() returned ERR
I already looked at this question, but it doesn't solve my problem. Is there a better way to accomplish this? Any suggestions are greatly appreciated.
The .txt file can be seen/downloaded here.
This isn’t “colored ASCII” — back in the day, we called it ANSI art. Curses doesn’t interpret the ANSI escape sequences that generate color and motion, nor does it pass them through cleanly to the terminal, even though it may (usually does, but it depends on the terminal) use them itself to render its output.
There are two basic approaches you can take: If you’re working within a broader curses framework and really want those features, you can interpret the ANSI sequences yourself, and render them in curses structures.
More simply, if you just want to put that image on the screen, and if you know that your terminal already supports ANSI, then skip curses, and just print each line via standard output.

How to preserve python colorama color output when using tee on Windows

In my Windows 10 scenario, I want to print arbitrary python console output (print(), sys.exit(), etc) both to console and a log file. I don't have control over some parts of the code (external python packages), so I cannot use some dedicated logging mechanism.
After some research I found the tool tee.exe in UnxUtils which does this task almost the way I want.
My problem is to preserve color as generated by python's colorama package. Is there any way to accomplish that? Currently, tee.exe strips away the color.
The answer I am looking does not have to rely on tee.exe, it's just the closest I got to a real solution. What I am looking for should do the following:
any command line output appears both in the command line and the log file (both STOUT and STDERR)
the output appears on the command line in real time. Bonus points if this is also true for the log file.
color is preserved on the command line. Bonus points if the log file does not contain any color-related artifacts.
What I have so far is this:
Python file teetest.py:
import sys
import colorama
print("Test")
print("2nd Test")
colorama.init(autoreset=True)
print(colorama.Fore.RED + 'Color Test')
colorama.deinit(autoreset=True)
sys.exit("Error_Test")
Batch file teetest.bat:
#echo off
python teetest.py 2>&1 | tee log.txt
pause
My output looks like this (command line and log file are identical, no color):
Test
2nd Test
Color Test
Error_Test
The solution I am looking for will print the above to the command line so the words Color Test are red.
Edit:
It seems that tee.exe is not at fault. Instead, colorama strips away the ANSI characters for color control by design, so the color is lost when passed through tee.exe.
From the colorama manual:
Colorama makes this work on Windows, too, by wrapping stdout, stripping ANSI sequences it finds (which would appear as gobbledygook in the output), and converting them into the appropriate win32 calls to modify the state of the terminal.
Colorama's init() function offers the parameter strip, which if False, does cause colorama to not strip away the ANSI characters. This in turn allows to write a custom tee.py that does the same as tee.exe, as outlined by user #martineau below. In it, we can call colorama and handle the color properly.
This might be a workable solution, but it still has the downside that I would have to replace all colorama init() calls with init(strip=False) in my original python code and that in turn would cause ANSI characters to appear in the output if the code was called without redirecting through tee.py.
This might actually be the closest we can get to a proper solution here. If anyone can offer other ideas, I'm all ears but I fear chances are slim.
I don't know how it will work with respect to colorama, but after being unsatisfied with several tee utilities for Windows that I found online, I ended up writing my own in Python (3.x).
You may have to modify it to suit your own needs, but it should be a good start.
mytee.py:
"""
Copies stdin to stdout (screen) *and* the specified file.
"""
import fileinput
import os
from pathlib import Path
import sys
SHOW_FULL_PATH = False
DIVIDER = True
DIV_CH = ' '
if len(sys.argv) != 2:
raise SystemExit('Usage: mytee <filepath>')
try:
inp = fileinput.input(()) # Read from stdin.
path = Path(sys.argv[1])
stdout_write = sys.stdout.write
stdout_flush = sys.stdout.flush
# Assumes .py in same dir as output file.
script = (f'{path.parent/path.stem}{path.suffix}' if SHOW_FULL_PATH else
f'{path.stem}{path.suffix}')
with open(path, 'w') as outp: # Write to specified file.
outp_write = outp.write
outp_flush = outp.flush
def write(line):
stdout_write(line)
outp_write(line)
def writeln(line):
write(line + '\n')
banner = f'"{script}"' if ' ' in script else f'-[{script}]-'
writeln(f'{banner}')
if DIVIDER:
writeln(f'{DIV_CH * len(banner)}')
for line in inp:
write(line)
if DIVIDER:
writeln(f'{DIV_CH * len(banner)}')
writeln('-[done]-')
finally:
inp.close() # Not sure this is really necessary.
sys.exit(0)

Print over Current Line in Python Shell

I'm trying to make a percentage text that displays a progress amount but i'm trying to avoid the percentages printing out like this:
Progress: 10%
Progress: 11%
Progress: 12%
Progress: 13%
How can erase and write over the current line? Iv'e tried using the \r and \b characters but neither seems to work. Every single thing I found before has been for either for Python 2 or Unix so i'm not even sure which of those is the problem (if even one of them) because i'm not using either. Does anyone know how I can do this with Python 3 running Windows 7? This is the unworking code that I have currently, but I've tried plenty of other things.
print('Progress: {}%'.format(solutions//possibleSolutions),flush=True,end="\r")
EDIT:
This is not a problem if I'm executing the program from command prompt so I don't think it is a problem with windows. I tried updating Python from what i was using previously (3.4.1) to the latest v3.4.3 and the issue is the same.
Heres a screenshot of the problem:
This is the best I can do at taking a screenshot of the issue. It appears as if each time I move the cursor farther to the left (passed one of the Progress:'s) that the gray area between the text and the cursor gets larger
EDIT 2: The problem is that IDLE does not support ASCII control codes. Solution: Use a different IDE.
You can use print:
print('Progress: {}%'.format(solutions),flush=True,end="\r")
You can't use '\r' and '\b' in IDLE. If you want to use it, try adding these lines at the start of your program:
import sys
sys.stdout = sys.__stdout__
and running idle with this batch script:
#echo off
echo Running IDLE...
py -m idlelib
then, you see output in cmd window and there are '\r' and '\b'.
Use the character '\r' for the print function. Default is '\n'.
'\r' stands for carriage return, '\n' means new line.
You can create a new class called Printer like this:
class Printer():
def __init__(self, data):
sys.stdout.write("\r\x1b[K"+data.__str__())
sys.stdout.flush()
Then, let's say you want to print the progress of a for loop:
for i in range(0, 100):
p = i * 100
output = "%d%% of the for loop completed" % p
Printer(output)

How to do partial carriage return

I want to replace the cmd output of my Python script, without overwriting the first three characters of the line. Here is what I mean:
How I want it:
>>>ID1 downloading
>>>ID1 converting
>>>ID1 finished
With /r:
>>>ID0 downloading
>>>converting
>>>finished
This isn't fundamentally a Python question; it's a standard output question.
The standard output doesn't work that way. The simplest thing is to just rewrite "ID1".
Otherwise you will need to move to something more advanced: either console-specific formatting commands (like ANSI), or a library like curses.
Try this. But this may not work in IDLE.
from time import sleep
from sys import stdout
stdout.write("\r%s" % "ID1 downloading")
stdout.flush()
sleep(1)
stdout.write("\r%s" % "ID1 converting ")
stdout.flush()
sleep(1)
stdout.write("\r%s" % "ID1 done ")
stdout.write("\r \r\n") # clean up

How to remove lines from stdout in python?

I have a program that grabs some data through ssh using paramiko:
ssh = paramiko.SSHClient()
ssh.connect(main.Server_IP, username=main.Username, password=main.Password)
ssh_stdin_host, ssh_stdout_host, ssh_stderr_host =ssh_session.exec_command(setting.GetHostData)
I would like to remove the first 4 lines from ssh_stdout_host. I've tried using StringIO to use readlines like this:
output = StringIO("".join(ssh_stdout_host))
data_all = output.readlines()
But I'm lost after this. What would be a good approach? Im using python 2.6.5. Thanks.
How to remove lines from stdout in python?
(this is a general answer for removing lines from the stdout Python console window, and has nothing to do with specific question involving paramiko, ssh etc)
see also: here and here
Instead of using the print command or print() function, use sys.stdout.write("...") combined with sys.stdout.flush(). To erase the written line, go 'back to the previous line' and overwrite all characters by spaces using sys.stdout.write('\r'+' '*n), where n is the number of characters in the line.
a nice example says it all:
import sys, time
print ('And now for something completely different ...')
time.sleep(0.5)
msg = 'I am going to erase this line from the console window.'
sys.stdout.write(msg); sys.stdout.flush()
time.sleep(1)
sys.stdout.write('\r' + ' '*len(msg))
sys.stdout.flush()
time.sleep(0.5)
print('\rdid I succeed?')
time.sleep(1)
edit
instead of sys.stdout.write(msg); sys.stdout.flush(), you could also use
print(msg, end='')
For Python versions below 3.0, put from __future__ import print_function at the top of your script/module for this to work.
Note that this solution works for the stdout Python console window, e.g. run the script by right-clicking and choosing 'open with -> python'. It does not work for SciTe, Idle, Eclipse or other editors with incorporated console windows. I am waiting myself for a solution for that here.
readlines provides all the data
allLines = [line for line in stdout.readlines()]
data_no_firstfour = "\n".join(allLines[4:])

Categories