Is it possible to create scrolling text in the Python command line by repeatedly updating the same line of text with small time.sleep() delays?
I believe that the \b (backspace) character can effectively move the cursor backward over text already written. I thought that combining this with end="" parameter in Python 3 might allow subsequent print commands to update a previous print command. With careful tracking of the length of the text and insertion of backspace and other characters it should be possible to create static lines of text that animate in place.
Unfortunately none of this works and even the \b character seems to do nothing:
word = input("Type something-> ")
print(word+"\b\b\bHello", end="")
print("New text")
Anyone got any ideas?
Many thanks,
Kw
Maybe you need carriage return, or \r. This takes you to the beginning of the line. It is the same effect as in a physical typewriter when you move your carriage to the beginning and overwrite whatever is there.
If you use this:
print("Hello", end=" ")
print("world")
The output will be:
Hello World
But if you use:
print("Hello", end="\r")
print("world")
The output will be only:
world
Related
I am new to coding. And I would like to know if there's a way for input function to not print newline character after the value is entered. Something like print function's argument end. Is there any way?
Well, you can't make input() trigger by anything besides 'Enter' hit (other way may be using sys.stdin and retrieving character one-by-one until you receive some stop marker, but it's difficult both for programmer and for user, I suppose). As a workaround I can the suggest the following: if you can know the length of line written before + length of user input, then you can use some system codes to move cursor back to the end of previous line, discarding the printed newline:
print("This is first line.")
prompt = "Enter second: "
ans = input(prompt)
print(f"\033[A\033[{len(prompt)+len(ans)}C And the third.")
\033[A moves cursor one line up and \033[<N>C moves cursor N symbols right. The example code produces the following output:
This is first line.
Enter second: USER INPUT HERE And the third.
Also note that the newline character is not printed by your program, it's entered by user.
name=input('Enter your name : ')
print('hello',name,end='')
I know about the end function which is abov
Suppose I have
print("line1")
print("line2")
print("\n\n line3")
and now I would like to print a "line4" over "line2".
How can I do it in python using only the print function?
You can use the ANSI escape sequences: wiki realpython
The CSI (control sequence introducer) in python is \033[
So for example, you could use the Cursor Up (CUU) sequence which is the CSI followed by {n}A where n is the number of times you want the cursor to move up.
So by defining
def cursorup(n):
print(f"\r\033[{n}A", end="")
either
print("line1")
print("line2")
print("\n\n line3")
cursorup(3)
print("line4")
or
print("line1")
print("line2")
print("\n\n line3")
print("\r\033[3A", end="")
print("line4")
will work.
The \r is to do a carriage return and move the cursor to the beginning of the current line. While end="" is so that the print function doesn't go to a new line after printing.
One way to write output in arbitrary locations is with another library such as curses.
I'm trying to print out a string with the end=\r to send the cursor back to beginning of the line, and overwrite the printed string using a for loop
This is the code thus far I got :
import time
print("░░░░░░░░░░░░░", end='\r')
for i in ("SECRET"):
print(i ,end='')
time.sleep(0.3)
Ideally, it should slowly overwrite some of the dotted pattern characters with characters from `"SECRET" every 0.3 seconds.
However, when run, the for loop instead iterates and prints characters on a single space, overwriting the characters it prints out itself, instead of advancing to the next available space, overwriting the dot pattern there and typing out the remaining characters in the string it iterates over
Removing the entire print statement associated with the dotted pattern characters allows the loop to function normally, printing out the string it iterates over properly, however, it is needed for the loop to print out the string and overwrite the dotted characters
Essentially, I want to overwrite some of the dotted pattern characters one by one using characters from the string the for loop iterates over, with the help of \r
I am on Ubuntu Studio
Screen output is line-buffered. This means that when you print something followed by a newline it appears on the screen immediately, but if you print something without a newline it might take a while to appear.
This is what's happening to you -- the output inside the for loop is not followed by a newline, so it doesn't appear onscreen immediately.
You can add flush=True to the print call to force the output to appear immediately instead of waiting:
print(i, end='', flush=True)
you can't overwrite characters in python.
you can, though, clear the whole screen by using os.system('cls') on windows or os.system('clear') on linux and unix.
here is the full code:
import time, os
output = '░░░░░░░░░░░░░'
for i in range(7):
print(output)
output = "SECRET"[:i]+output[i:]
time.sleep(0.3)
if os.name == 'nt': #on windows
os.system("cls")
else:
os.system("clear") #not on windows
print(output)
also, this will only work when you are not running from the shell and if you want to see the full output, write time.sleep(1) at the end.
I'm working on writing a simple Hangman game in Python from what I know so far (I'm doing Learn Python the Hard Way) and so far I have this:
from sys import argv
import random
script_name, dict_file = argv
hang_list = open(dict_file).read().splitlines()
hang_list = filter(None, hang_list)
word = random.choice(hang_list)
guesses = ''
def compare_words():
global guesses
new_word = ''
for char in word:
if char in guesses:
new_word += char
else:
new_word += "_"
return new_word
def test_letter():
global guesses
letter = raw_input("Guess a letter: ")
guesses += letter
new_word = compare_words()
print "\nCurrent guesses: %s" % guesses
print "%s\n\n" % new_word
if new_word == word:
print "You won!"
else:
test_letter()
test_letter()
I've yet to implement the scoring system (piece of cake) but I have an issue with the layout. As you can tell, this will print "Current guesses: " and the new word each time; however, what I want is four lines that look like:
Guess a letter:
Guesses: abczy
__c__b_
And have those three lines keep updating. However, I am having trouble figuring out how to make the print replace stdout. I believe I need to use the \r escape character, yet I've tried placing that in various places but can't get it to work. So, how should I modify this to get it to replace? I would prefer not to just clear, as then it still makes things a bit messy; I want to just replace what's there. Thanks!
It would be a bit tricky to make this work for all terminals, but if yours understands ANSI escape codes like mine does, this might work:
...
if new_word == word:
print "You won!"
else:
print '\033[F'*7
print ' '*17 + '\b'*17 + '\033[F'
test_letter()
This relies on the ANSI code F: move the cursor up one line; backspaces (\b) alone have no effect once the beginning of the line is reached.
The first print takes you back up to the input line and the second deletes the character that was previously entered.
You can use the escape characters \033c and this will erase the code in a terminal window and put the cursor at the top left.
For example this code:
import time
print("text 1")
time.sleep(1)
print('\033c')
time.sleep(1)
print("text 2")
This code will print "text 1" wait one second, clear the console, wait one second and then print "text 2".
So you could use the code
def test_letter():
print("\033c")
global guesses
letter = raw_input("Guess a letter: ")
guesses += letter
new_word = compare_words()
print "\nCurrent guesses: %s" % guesses
print "%s\n\n" % new_word
if new_word == word:
print "You won!"
else:
test_letter()
What this code will do is clear the console, ask the person to guess a number, display that four line piece of code that you wanted and then clear the console again.
I hope this helps!
If you want to replace the content of a specific line, from a specific position, you can use ANSI Escape Codes. To do this, make sure that you're using stdout.write() rather than print(). You can access this method by using the following import statement:
from sys import stdout
Then, in order to navigate the "cursor" (where text printed with this method will go), use the escape code \u001b[<L>;<C>H (or \u001b[<L>;<C>F where <L> and <C> represent the respective line number and character index of the desired position. For example, if you wanted to set the cursor to line 3; character 2, you would do the following.
stdout.write(u"\u001b[3;2H")
Note the u proceeding the double-quoted string. This is required in Python 2.x, since it contains special characters, but can be omitted in Python 3 and above.
Once you have set the cursor to be at the desired position, anything you write will replace the characters that currently reside there. This is important, because if the replacement string is shorter than the original, you may end up with trailing legacy characters. The simplest way to deal with this is to pad the printing string in spaces.
After doing this you should probably move the cursor back to the end of stdout, using the same method, and flush the output with stdout.flush().
Example
Let's say I had the following output on the terminal:
Name: Shakespeare
Score: 0
Some text...
I could change the score to 1 by running the following:
stdout.write(u"\u001b[2;8H")
stdout.write("1")
stdout.write(u"\u001b[5;0H")
stdout.flush()
Again, the u is optional in Python 3 and up.
Notes
This line-and-character-number method applies to all output currently being displayed in the terminal. This means that if you have anything left from another program or command, for example
$ python game.py
so it is best to clear the output at the start of your program, with something like print(u"\033c"), or os.system("clear"), otherwise you may end up writing to the wrong line.
Also, if your going to use stdout.write() anywhere else, remember to put \n at the end if you want to go to the next line.
The \r character is a carriage return, which means it will return the cursor to the start of the current line. That's OK if you want to redraw the line the cursor is on, but no good if you want to redraw other lines.
To do what you want, you need to use a terminal library like curses on Linux or the console API on Windows. If you are just working on Linux and want a simpler way to access colours, cursor movement and input without echo, you could do worse than try out blessed (https://pypi.python.org/pypi/blessed/).
If you need a cross platform API to do this sort of thing, there is no pure Python way to handle it all yet, but I am working on one. The Screen class in https://github.com/peterbrittain/asciimatics cover all the features above in a cross-platform manner.
I have this code in Python
inputted = input("Enter in something: ")
print("Input is {0}, including the return".format(inputted))
that outputs
Enter in something: something
Input is something
, including the return
I am not sure what is happening; if I use variables that don't depend on user input, I do not get the newline after formatting with the variable. I suspect Python might be taking in the newline as input when I hit return.
How can I make it so that the input does not include any newlines so that I may compare it to other strings/characters? (e.g. something == 'a')
You are correct - a newline is included in inputted. To remove it, you can just call strip("\r\n") to remove the newline from the end:
print("Input is {0}, including the return".format(inputted.strip("\r\n")))
This won't cause any issues if inputted does not have a newline at the end, but will remove any that are there, so you can use this whether inputted is user input or not.
If you don't want any newlines in the text at all, you can use inputted.replace("\r\n", "") to remove all newlines.
Your problem is actually Eclipse. Assuming that you use PyDev, I was able to reproduce the problem. When entering something in the Eclipse console, the problem occurs as described in your question. But when directly executing the very same script with the Python 3.1.1 interpreter, inputted does not include a newline character.
I investigated the Python source code and found out input() uses GNU readline if stdin is interactive (i.e. a TTY or prompt, however you want to call it), but falls back to the .readline() method of the stdin object if necessary. Then, if the result of readline ends with \n, that character is removed. Note: No CR-LF or LF-CR handling here (in the fallback case)!
So I wrote this little script to see what actually happens:
import sys
from io import StringIO
for stdin in [sys.stdin, StringIO("test\r\ntest\r\n")]:
sys.stdin = stdin
print("readline returns this: " + repr(sys.stdin.readline()))
inputted = input("Enter in something: ")
print("inputted: " + repr(inputted))
print("inputted is printed like this: --> {0} <--".format(inputted))
It first executes the code with the normal stdin (console or Eclipse console) and then with a prepared stdin containing the text test\r\ntest\r\n.
Try and run the script in Eclipse - you must enter a string twice. The conclusion: Pressing Enter in the Eclipse console will produce CR-LF ("\r\n"). Printing "\r" in the Eclipse console will jump to the next line.
On the other side, running it in the Windows console will produce the expected output: input() returns a string without a newline at the end because (I guess) GNU readline is used. With the prepared stdin StringIO("test\r\n"), the input() result is "test\r" as in Eclipse (although not printed as newline).
Hope this all makes sense... but what I still don't know is if that is expected behavior of Eclipse.
If you only want to stript the last line endings, you could use rstrip.
inputted.rstrip ("\r\n")
inputted = inputted.strip()
Edit: As noted, this will kill all whitespace at the start and end. A way to get rid of only the trailing newline is:
import re
inputted = re.sub("[\n\r]+$", "", inputted)