I'm trying to make a simple function that will type out a string letter by letter, such as in a game, where text scrolls.
Here's what my code looks like:
import time
def ScrollingText(s):
s=str(s)
for letter in s:
print(letter, end="")
time.sleep(.05)
print("") # newline at the end
if __name__=='__main__':
ScrollingText("Hello World!")
However when I run, it waits, then dumps out the whole string at once. I'm new to python (and this forum as well) so if anyone can point me in the right direction and show me what I'm missing here in time.sleep I'd greatly appreciate it. Thanks!
As you do not have a newline in your string, python will buffer it. You have to explicitly flush the output after each character like this:
import sys
import time
def ScrollingText(s):
s=str(s)
for letter in s:
print(letter, end="")
sys.stdout.flush()
time.sleep(.05)
print("") # newline at the end
For better performance, I/O is usually buffered, that is, python will collect the data you print until it can send it out as a large block (this also goes for file I/O by the way). By calling flush() on sys.stdout (which is the file object where print writes to by default), you force python to send your data to the operating system (and that will send it to your terminal).
Related
I have a case where my script calls readlines() on sys.stdin followed by a call to input(), but that last call won't work.
Here's my script:
import click
#click.command()
#click.argument('data', type=click.File())
def inp(data):
lines = data.readlines()
print('Read {} lines. Continue?'.format(len(lines)))
choice = input().lower()
print("You said '{}'.".format(choice))
if __name__ == '__main__':
inp()
The script works fine if the user specified an actual file argument on the command line, but not if they pipe input data and use the -:
head -n10 data.txt | python3 script.py -
Read 10 lines. Continue?
Aborted!
I need to support the - reading followed by input() -- how can I make it work?
to save our time, u are free to jump to the bottom to see the conclusion
Start
I am searching for the answer to the question as u did.
At First, I think the following will solve the problem...
multiple piped inputs without EOF
How to use `input()` after reading a file from `stdin`?
however, it does not succeed in my computer,
and thus, I try to figure out how to exactly clear the EOF mark...
Terminate/Break/Abort Python Console/sys.stdin readline()
How do you terminate/break/abort a Python console/sys.stdin readline()?
flush
How to flush the input stream in python?
In C Language, we use fflush(stdin), ..etc to clear the buffer
In C++ Language, we use std::cin.get(), ..etc to clear the buffer
But How About Python Language ??
After Searching For 2 hours, I realize Python so far does not provide this function, clear the buffer/EOF in the stdin
And the answers available online which succeed like this one
How to use `input()` after reading a file from `stdin`? are only suitable for Linux System
After Reading This
https://www.twblogs.net/a/5c0ac824bd9eee6fb21399d4
*I am so sure(i think) that Python in Windows cannot use input after stdin is taken by file
However, does that mean, we should batter the sys.stdin method?
No, Obviously, or I won't spend my time here typing...
And Here Is My Final Method To Solve The Problem
re-Create Your Own Input as follow
def new_input(): ## version 1
import msvcrt
str_=''
c=msvcrt.getche()
while ord(c)!=3 and ord(c)!=4 and ord(c)!=26 and ord(c)!=13:
# print(ord(c))
str_ = str_+str(c)[2:-1]
c=msvcrt.getche()
return str_
def new_input(interact_string_): ## version 2
import msvcrt
print(interact_string_, end ="")
str_=''
c=msvcrt.getche()
while ord(c)!=3 and ord(c)!=4 and ord(c)!=26 and ord(c)!=13:
# print(ord(c))
str_ = str_+str(c)[2:-1]
c=msvcrt.getche()
return str_
def new_input(interact_string_): ## version 3
import msvcrt
import os
print(interact_string_, end ="")
str_=''
print('')
c=msvcrt.getche()
while ord(c)!=3 and ord(c)!=4 and ord(c)!=26 and ord(c)!=13:
os.system('cls')
print(interact_string_, end ="")
# print(ord(c))
if ord(c)==8:
str_ = str_[0:-1]
print(str_)
else:
str_ = str_+str(c)[2:-1]
print(str_)
c=msvcrt.getche()
# print(str_)
return str_
Here, are the meanings of numbers
ord(c)==3 will be the key of Ctrl+C
ord(c)==4 will be the key of Ctrl+D
ord(c)==26 will be the key of Ctrl+Z
ord(c)==13 will be the key of Enter
ord(c)==22 will be the key of Ctrl+C
ord(c)==8 will be the key of Backspace
And it will still be convenient to using if-structure to react with, and returns the final string
Conclusion:
after using stdin to readlines()/readline()/read() to read a file you cannot ever using stdin to input/output in Windows System
while, in the same case, the Same Problem on Linux System is solvable, and the answer will be up there...
In my opinion and under my research, The Best Answer to Windows will be mine (of course will I say that..) . ++==> re-Create your input()
Thanks For Your Reading, constructive advice and polite comment is welcomed. Anyone With a Better Idea or Solve is welcome too.
like it if u enjoy the journey with me while reading my adventure to finding answer
[printing slowly (Simulate typing)
I got my answer from the link above but it only works when you put the string as a parameter when calling function.
I want the code to print slowly every time when I use print().
is it possible?
Yes, you can do it like this, however, I think it's not a good idea:
import time
def dprint(string):
for letter in string:
__builtins__.print(letter,end = '', flush=True)
time.sleep(.1)
__builtins__.print("")
print = dprint
print("something")
Yes, you can do it using the stdout version as below.
import sys, time
def print(s):
for letter in s:
sys.stdout.write(letter)
time.sleep(.1)
print("Foo")
Changing the default behaviour of print() is not recommended and was only introduced for purpose of porting Python 2 programs easily. Moreover overloading the print function without a special parameter will make the default functionality of print() moot.
Create another function with adds a delay to the prints. Also remember that you cannot use print() because it appends a new line. You’ll have to you sys.stdout.write()
So a basic function would look like:
def typrint(x):
for i in len(x):
sys.stdout.write(x[i])
sleep(0.05)
sys.stdout.write(“\n”)
Check this article to see why Python updated print() to a function
I am using this as the solution for the problem,
import sys,time
def delay(str):
for i in str:
sys.stdout.write(i)
sys.stdout.flush()
time.sleep(0.04)
Note: You need to add in every print statement or here "delay" statement "\n".
This question already has an answer here:
Why doesn't print output show up immediately in the terminal when there is no newline at the end?
(1 answer)
Closed last month.
I was trying to create a progress-like thing by printing a dot every second on the same line. Something like "Fishing.....". This is what I used:
import time
print('Fishing', end='')
for i in range(5):
time.sleep(1)
print('.', end='')
But it waits for 5 seconds and prints Fishing..... all at once. But when I don't use the end='', it prints dots every second, but on separate lines like so
Fishing.
.
.
.
.
My questions:
Why does print behave this way?
How can I print a dot every second, but on the same line?
Why does print behave this way?
This has less to do with print and more with your terminal. For performance reasons, the text only gets "flushed" everytime there's a newline character, and not one character at a time.
How can I print a dot every second, but on the same line?
By "flushing" the standard output manually everytime you printed something:
import time
import sys
print('Fishing', end='')
sys.stdout.flush()
for i in range(5):
time.sleep(1)
print('.', end='', flush=True) # another way
If you need this all the time, you could define a seperate flushing print function:
from functools import partial
myprint = partial(print, end='', flush=True)
myprint('Fishing')
for i in range(5):
time.sleep(1)
myprint('.')
This is because print is considered to be an expensive operation: it will usually wait until it has to print a lot of data, or until a new line is encountered (usually only if output is written to a terminal):
Output buffering is determined by file. Use file.flush() to ensure, for instance, immediate appearance on a screen.
Evidently it is not expensive in the sense that it takes minutes: but asking the terminal operator to print new content, or the operating system to write data to a file (in case of I/O redirection) is still not "lightning fast".
You can force to print all data that is still in the queue, by using a flush on the standard output channel.
use
print('.', end="", flush=True)
I'd like to print a string to command line / terminal in Windows and then edit / change the string and read it back. Anyone knows how to do it? Thanks
print "Hell"
Hello! <---Edit it on the screen
s = raw_input()
print s
Hello!
You could do some ANSI trickery to make it look like you are editing on screen. Check out this link (also similar to this SO post on colors).
This would only work on certain terminals and configurations. ymmv.
This python script worked in my Cygwin terminal on Win7:
print 'hell'
print '\033[1A\033[4CO!'
Ends up printing hellO! on one line. The 2nd print moves the cursor up one line (Esc[1A) then over 4 characters (Esc[4C]) and then prints the 'O!'.
It wouldn't let you read it back though... only a 1/2 answer.
I had this same use-case for a command-line application.
Finally found a hack to do this.
# pip install pyautogui gnureadline
import pyautogui
import readline
from threading import Thread
def editable_input(text):
Thread(target=pyautogui.write, args=(text,)).start()
modified_input = input()
return modified_input
a = editable_input("This is a random text")
print("Received input : ", a)
The trick here is use pyautogui to send the text from keyboard. But we want to do this immediately after the input(). Since input() is a blocking call, we can run the pyautogui command in a different thread. And have an input function immediately after that in the main thread.
gnureadline is for making sure we can press left and right arrow keys to move the cursor in a terminal without printing escape characters.
Tested this on Ubuntu 20, python 3.7
raw_input accepts a parameter for a "prompt message", so use that to output the message, and then prepend it to what you get back. However, this won't allow you to backspace into the prompt, because it's a prompt and not really part of the input.
s = "Hell" + raw_input("Hell")
print s
os.sys.stdout is write only, but you can erase some characters of the last line with \b or the whole line with \r, as long as you did not write a carriage return.
(however, see also my question about limitations to the standard python console/terminal)
I once made some output exercise (including a status bar) to write,erase or animate if you will, perhaps it is helpfull:
from __future__ import print_function
import sys, time
# status generator
def range_with_status(total):
n=0
while n<total:
done = '#'*(n+1)
todo = '-'*(total-n-1)
s = '<{0}>'.format(done+todo)
if not todo:
s+='\n'
if n>0:
s = '\r'+s
sys.stdout.write(s)
sys.stdout.flush()
yield n
n+=1
print ('doing something ...')
for i in range_with_status(10):
time.sleep(0.1)
print('ready')
time.sleep(0.4)
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(4)
If it's for your own purposes, then here's a dirty wee hack using the clipboard without losing what was there before:
def edit_text_at_terminal(text_to_edit):
import pyperclip
# Save old clipboard contents so user doesn't lose them
old_clipboard_contents = pyperclip.paste()
#place text you want to edit in the clipboard
pyperclip.copy(text_to_edit)
# If you're on Windows, and ctrl+v works, you can do this:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys("^v")
# Otherwise you should tell the user to type ctrl+v
msg = "Type ctrl+v (your old clipboard contents will be restored):\n"
# Get the new value, the old value will have been pasted
new_value= str(raw_input(msg))
# restore the old clipboard contents before returning new value
pyperclip.copy(old_clipboard_contents )
return new_value
Note that ctrl+v doesn't work in all terminals, notably the Windows default (there are ways to make it work, though I recommend using ConEmu instead).
Automating the keystrokes for other OSs will involve a different process.
Please remember this is a quick hack and not a "proper" solution. I will not be held responsible for loss of entire PhD dissertations momentarily stored on your clipboard.
For a proper solution there are better approaches such as curses for Linux, and on Windows it's worth looking into AutHotKey (perhaps throw up an input box, or do some keystrokes/clipboard wizardry).
Certain applications like hellanzb have a way of printing to the terminal with the appearance of dynamically refreshing data, kind of like top().
Whats the best method in python for doing this? I have read up on logging and curses, but don't know what to use. I am creating a reimplementation of top. If you have any other suggestions I am open to them as well.
The simplest way, if you only ever need to update a single line (for instance, creating a progress bar), is to use '\r' (carriage return) and sys.stdout:
import sys
import time
for i in range(10):
sys.stdout.write("\r{0}>".format("="*i))
sys.stdout.flush()
time.sleep(0.5)
If you need a proper console UI that support moving the pointer etc., use the curses module from the standard library:
import time
import curses
def pbar(window):
for i in range(10):
window.addstr(10, 10, "[" + ("=" * i) + ">" + (" " * (10 - i )) + "]")
window.refresh()
time.sleep(0.5)
curses.wrapper(pbar)
It's highly advisable to use the curses.wrapper function to call your main function, it will take care of cleaning up the terminal in case of an error, so it won't be in an unusable state afterwards.
If you create a more complex UI, you can create multiple windows for different parts of the screen, text input boxes and mouse support.
As most of the answers have already stated, you really have little option on Linux but to use ncurses. But what if you aren't on Linux, or want something a little more high-level for creating your terminal UI?
I personally found the lack of a modern, cross-platform terminal API in Python frustrating, so wrote asciimatics to solve this. Not only does it give you a simple cross-platform API, it also provides a lot of higher level abstractions for UI widgets and animations which could be easily used to create a top-like UI.
Sending output to the terminal via the print() command can be done without scrolling if you use the attribute "end".
The default is end='\n' which is a new line.
To suppress scrolling and overwrite the whole previous line, you can use the RETURN escape which is '\r'.
If you only want to rewrite the last four characters, you can use a few back-spaces.
print(value, "_of_", total, end='\r')
NOTE
This works for the standard system terminal. The terminal emulator in some tools like IDLE has an error and the '\r' does not work properly, the output is simply concatenated with some non-printable character between.
BONUS INFORMATION FOR print()
In the example above, the spaces on each side of "of" are meant to insure white-space between my values and the word "of". However, the default separater of the print() is a " " (space) so we end up with white space between the value and underscore of "_of_".
>> print (value, "_of_", total, end='\r')
8 _of_ 17
The sepparator attribute, sep, can be used to set character between printed items. In my example, I will change it to a null string ('') to make my output suit my needs.
>> print (value, "_of_", total, sep='', end='\r')
8_of_17
I hacked this script using curses. Its really a ad-hoc solution I did for a fun. It does not support scrolling but I think its a good starting point if you are looking to build a live updating monitor with multiple rows on the terminal.
https://gist.github.com/tpandit/b2bc4f434ee7f5fd890e095e79283aec
Here is the main:
if __name__ == "__main__":
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
curses.start_color()
curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(4, curses.COLOR_CYAN, curses.COLOR_BLACK)
try:
while True:
resp = get_data()
report_progress(get_data())
time.sleep(60/REQUESTS_PER_MINUTE)
finally:
curses.echo()
curses.nocbreak()
curses.endwin()
When I do this in shell scripts on Unix, I tend to just use the clear program. You can use the Python subprocess module to execute it. It will at least get you what you're looking for quickly.
import time
for i in range(10):
print('\r{}>'.format('='*i), end='')
time.sleep(0.5)
I don't think that including another libraries in this situation is really good practice. So, solution:
print("\rCurrent: %s\t%s" % (str(<value>), <another_value>), end="")