Strange print behavior with time.sleep in python [duplicate] - python

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)

Related

How to delete print

Here's the problem. In the following code, I wish to produce a 'moving cursor' effect. Here's the code:
sys.stdout.write('\033[2K\033[1G')
time.sleep(2)
print ('virus_prevention.fix.virus.|attempt_enter')
time.sleep(2)
sys.stdout.write('\033[2K\033[1G')
print ('virus_prevention.fix.virus|.attempt_enter')
time.sleep(0.1)
sys.stdout.write('\033[2K\033[1G')
print ('virus_prevention.fix.viru|s.attempt.enter')
time.sleep(0.1)
sys.stdout.write('\033[2K\033[1G')
print('virus_prevention.fix.vir|us.attempt.enter')
time.sleep(0.1)
sys.stdout.write('\033[2K\033[1G')
print ('virus_prevention.fix.vi|rus.attempt.enter')
time.sleep(0.1)
sys.stdout.write('\033[2K\033[1G')
print ('virus_prevention.fix.v|irus.attempt.enter')
time.sleep(0.1)
sys.stdout.write('\033[2K\033[1G')
print ('virus_prevention.fix.|virus.attempt.enter')
time.sleep(2)
sys.stdout.write('\033[2K\033[1G')
print ('virus_prevention.fix|virus.attempt.enter')
And this is the output:
[2K[1Gvirus_prevention.fix.virus.|attempt_enter
[2K[1Gvirus_prevention.fix.virus|.attempt_enter
[2K[1Gvirus_prevention.fix.viru|s.attempt.enter
[2K[1Gvirus_prevention.fix.vir|us.attempt.enter
[2K[1Gvirus_prevention.fix.vi|rus.attempt.enter
[2K[1Gvirus_prevention.fix.v|irus.attempt.enter
[2K[1Gvirus_prevention.fix.|virus.attempt.enter
And the sys.stdout.write doesn't really help. It just ends up with added text on the front of the current text. So if there are solutions anyone is willing to share (Python 3) please do. (I did have a solution which used repeatedly clearing the screen via os.system('clear'), which I don't really want to use.)
sys.stdout.write is a good start, but you also need to pass a "carriage return" '\r' to jump to the beginning of the line. This will overwrite the old characters with the next call:
for i in range(10):
sys.stdout.write(str(i)+'\r')
time.sleep(1)
If the new line is shorter than the previous line, you will still see the additional characters of the previous line. As a fix you can add some additional spaces to overwrite them.
The main difference between sys.stdout.write and print is, that print will automatically append a linebreak (\n). This is why you see the sys.stdout.write infront of the next printed line.
Running this in an interactive Python session has some weird side effects, but it is fine if you use it in a Python script. Also, make sure not to have any other print() commands inbetween. This only works on the current line, and any '\n' creates a new line.
sys.stdout.write('virus_prevention.fix.virus.|attempt_enter\r')
time.sleep(2)
sys.stdout.write('virus_prevention.fix.virus|.attempt_enter\r')
print() # create a linebreak at the end
This is similar to what you wish to achieve, you will need to adjust it to work with where you want the 'cursor' to show
import time
displayText = "Python"
character = '|'
for i in range(len(displayText)+1):
print(displayText[:i] + character + displayText[i:], end='\r')
time.sleep(.2)
input()
This will give the desired effect when executed via console/command prompt; but not through Python's IDLE Shell.

printing every thing slowly (Simulate typing)

[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".

How to clear only last one line in python output console?

I am trying to clear only last few line from output console window. To achieve this I have decided to use create stopwatch and I have achieved to interrupt on keyboard interrupt and on enter key press it creates lap but my code only create lap once and my current code is clearing whole output screen.
clear.py
import os
import msvcrt, time
from datetime import datetime
from threading import Thread
def threaded_function(arg):
while True:
input()
lap_count = 0
if __name__ == "__main__":
# thread = Thread(target = threaded_function)
# thread.start()
try:
while True:
t = "{}:{}:{}:{}".format(datetime.now().hour, datetime.now().minute, datetime.now().second, datetime.now().microsecond)
print(t)
time.sleep(0.2)
os.system('cls||clear') # I want some way to clear only previous line instead of clearing whole console
if lap_count == 0:
if msvcrt.kbhit():
if msvcrt.getwche() == '\r': # this creates lap only once when I press "Enter" key
lap_count += 1
print("lap : {}".format(t))
time.sleep(1)
continue
except KeyboardInterrupt:
print("lap stop at : {}".format(t))
print(lap_count)
when I run
%run <path-to-script>/clear.py
in my ipython shell I am able to create only one lap but it is not staying for permanent.
To clear only a single line from the output :
print ("\033[A \033[A")
This will clear the preceding line and will place the cursor onto the beginning of the line.
If you strip the trailing newline then it will shift to the previous line as \033[A means put the cursor one line up
I think the simplest way is to use two print() to achieve clean the last line.
print("something will be updated/erased during next loop", end="")
print("\r", end="")
print("the info")
The 1st print() simply make sure the cursor ends at the end of the line and not start a new line
The 2nd print() would move the cursor to the beginning of the same line and not start a new line
Then it comes naturally for the 3rd print() which simply start print something where the cursor is currently at.
I also made a toy function to print progress bar using a loop and time.sleep(), go and check it out
def progression_bar(total_time=10):
num_bar = 50
sleep_intvl = total_time/num_bar
print("start: ")
for i in range(1,num_bar):
print("\r", end="")
print("{:.1%} ".format(i/num_bar),"-"*i, end="")
time.sleep(sleep_intvl)
The codes shared by Ankush Rathi above this comment are probably correct, except for the use of parenthesis in the print command. I personally recommend doing it like this.
print("This message will remain in the console.")
print("This is the message that will be deleted.", end="\r")
One thing to keep in mind though is that if you run it in IDLE by pressing F5, the shell will still display both messages. However, if you run the program by double clicking, the output console will delete it. This might be the misunderstanding that happened with Ankush Rathi's answer (in a previous post).
I know this is a really old question but i couldn't find any good answer at it. You have to use escape characters. Ashish Ghodake suggested to use this
print ("\033[A \033[A")
But what if the line you want to remove has more characters than the spaces in the string?
I think the better thing is to find out how many characters can fit in one of your terminal's lines and then to add the correspondent number of " " in the escape string like this.
import subprocess, time
tput = subprocess.Popen(['tput','cols'], stdout=subprocess.PIPE)
cols = int(tput.communicate()[0].strip()) # the number of columns in a line
i = 0
while True:
print(i)
time.sleep(0.1)
print("\033[A{}\033[A".format(' '*cols))
i += 1
finally I would say that the "function" to remove last line is
import subprocess
def remove():
tput = subprocess.Popen(['tput','cols'], stdout=subprocess.PIPE)
cols = int(tput.communicate()[0].strip())
print("\033[A{}\033[A".format(' '*cols))
For Python 3's, using f-String.
from time import sleep
for i in range(61):
print(f"\r{i}", end="")
sleep(0.1)
Found a solution on this page that works. Here is the helper function:
import sys
def delete_last_line():
"Deletes the last line in the STDOUT"
# cursor up one line
sys.stdout.write('\x1b[1A')
# delete last line
sys.stdout.write('\x1b[2K')
I hope it helps someone.
None of the other answers worked for me. Putting print("Sentence to be overwritten", end='\r') would instantly clear my sentence and it would never be visible to begin with. I'm using PyCharm on a Mac if that could be making the difference. What I had to do is the following:
from time import sleep
print("Sentence to be overwritten", end='')
sleep(1)
print("\r", end='')
print("Sentence to stay")
end='' makes it so the print doesn't automatically put a '\n' character at the end. Then print("\r", end='') will put the cursor at the beginning of the line. Then the 2nd print statement will be printed in the same spot as the first, overwriting it.
If you intend to delete certain line from the console output,
print "I want to keep this line"
print "I want to delete this line",
print "\r " # this is going to delete previous line
or
print "I want to keep this line"
print "I want to delete this line\r "

Python: time.sleep functions unexpectedly?

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).

Printing at timed intervals

I know how to utilize time.sleep(), but I was curious how to print something like so:
"hey...(pause)...you...(pause)....there"
where the 'pause' is some time.sleep() interval. I can only print these intervals on separate lines. Is there a way to keep it all on one line?
In python 2:
print "hey...",
time.sleep(0.5)
In python 3:
print("hey...", end=' ')
time.sleep(0.5)
In a print statement in python2.x, you can suppress the newline with a trailing comma:
print "hey...",
time.sleep(1)
print "...you...",
time.sleep(1)
print "....there"
On python3.x (or with from __future__ import print_function enabled), you use the end keyword to the print function:
print("hey...", end="")
etc.
Or, finally, you can always1 write to the sys.stdout stream:
import sys
sys.stdout.write("hey...")
time.sleep(1)
...
The advantage here is that you can flush it explicitly:
sys.stdout.flush()
1technically not always. sys.stdout could be replaced by something else: sys.stdout = 3 -- But making a mess without cleaning up after yourself is terribly rude. However, if you ever find yourself in that situation, sys.__stdout__ is a backup ;-).

Categories

Resources