Whole program typing effect - python

I'm making a text adventure game in Python 3. Is there any way to add a typing effect to any text that gets printed without repeating a command after every line?

Assuming that with "typing effect" you mean that the messages should slowly appear one character at a time, you can define a function that iterates the given message and prints it one character at a time, using time.sleep in between to wait a bit. Make sure to flush the buffer after each character.
import time
def slow_print(msg):
for c in msg:
print(c, end="", flush=True)
time.sleep(0.1)
print()
slow_print("Hello World!")
If you really want to apply this for each and every output in your game (which I'd really not recommend) you can overwrite the print function, keeping a reference to the original print function to use within your new slow print function.
original_print = print
def slow_print(msg):
# same as above, but using original_print instead of print
print = slow_print
print("Hello World!")
You could also just def print(...) directly, but I'd recommend defining it as a separate function and then assigning it to print. This way, you can still make this optional, as this will most likely annoy the player after the first few minutes.

I assume you want the characters to appear as if someone were typing them so I'll just assume that
Import modules
import os
import sys
import time
from colr import color
Define your function
def function_name(phrase,speed,r_value,g_value,b_value):
for char in phrase:
sys.stdout.write(color(char, fore=(r_value,g_value,b_value)))
sys.stdout.flush()
time.sleep(speed)
Test the function
function_name("Hello",0.05,0,255,0)
#prints the phrase "Hello" in green text
Alternatively you could write the function using the threading library, which would allow users to skip the typing effect if they so wish.
import time, threading, os, sys, tty, termios
from colr import color
def slow_type_interrupt(phrase,speed,r_value,g_value,b_value):
done = False # this acts as the kill switch, using if statements, you can make certain button presses stop the message printing and outright display it
def type_out():
for char in phrase:
if done:
break
sys.stdout.write(color(char,fore=(r_value,g_value,b_value)))
sys.stdout.flush()
time.sleep(speed)
os.system('clear')
print(color(phrase,fore=(r_value,g_value,b_value)))
t = threading.Thread(target=type_out)
t.start()
def getkey():
ky = sys.stdin.fileno()
Ab = termios.tcgetattr(ky)
try:
tty.setraw(sys.stdin.fileno())
key = sys.stdin.read(1)
finally:
termios.tcsetattr(ky, termios.TCSADRAIN, Ab)
return key
while not done:
key_press = getkey()
if key_press == 'a': #You can replace a with whatever key you want to act as the "kill key"
done = True
os.system('clear')
print(color(phrase,fore=(r_value,g_value,b_value)))
slow_type_interrupt("Hello this is a test. Pressing 'a' will end this and immediatley display the message",.05,0,255,0)
As I mentioned in the comments of the code, a can be replaced by whatever you want. The reason I use this particular method for retrieving keypresses is because it works on almost anything that runs Python. I would suggest reading up on some other ways to retrieve keyboard inputs.
Hope I could help :)

Related

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

Loading animation in python

Im new to python and was wondering how to make a loading animation while my program runs. I need this because I don't want users thinking that the program is caught in a dead loop. I prefer a something like...
Loading...(with the dots disappearing and reappearing one by one)
Thanks!
If your output window supports the carriage return character, you can print it to make the cursor return to the beginning of the current line (provided you end your print statement with a comma, so a newline character isn't automatically printed). Then subsequent prints will overwrite what was already printed. You can use this to do very simple one line animation. Example:
import time
print "Starting program."
print "Loading ",
time.sleep(1) #do some work here...
print "\rLoading. ",
time.sleep(1) #do some more work here...
print "\rLoading.. ",
time.sleep(1) #do even more work...
print "\rLoading...",
time.sleep(1) #gratuitious amounts of work...
print "\rLoading ",
... Where time.sleep(1) is a placeholder representing the actual work you want to do.
Result:
Starting program.
Loading
Then, one second later:
Starting program.
Loading.
Then, one second later:
Starting program.
Loading..
Then, one second later:
Starting program.
Loading...
Then, one second later:
Starting program.
Loading
etc.
Compatibility note: in 3.X, print is no longer a statement, and the "end with a comma" trick no longer works. Instead, specify the end parameter:
print("\rLoading...", end="")
The most proper way I can think of to do it would be using threading.
You would initiate a thread that starts displaying some indication that the program is doing something and then open a new thread that actually does the work.
When the thread doing the work finished then you can move on with whatever else the program does.
This looks ok when ran in windows command prompt, not sure how linux will like it:
import threading
import time
import os
import queue
q = queue.Queue()
q.put(False)
class counter(object):
def __init__(self):
wait_label = "Loading"
self.stop_flag = q.get()
while not self.stop_flag:
try:
self.stop_flag = q.get_nowait()
except:
pass
os.system('cls') # might need to change this command for linux
wait_label += "."
print(wait_label)
time.sleep(1)
class other(counter):
def __init__(self):
time.sleep(15)
q.put(True)
counter_thread = threading.Thread(None, counter)
counter_thread.start()
other_thread = threading.Thread(None, other)
other_thread.start()
To reduce the code length, we can loop it.
import time
# flag variable to print the dots and it's value increases inside the while loop.
flag = 1
# To print the dots we use while loop. In total, 4 dots will be printed.
while flag < 5:
print("\rLoading, Please Wait " + ("." * flag), end=" ")
time.sleep(1)
flag = flag + 1

More efficient text-based loading bar

I am making a program that has a "loading bar" but I can't figure out how to make the code shorter. This might be a simple fix for all I know, but for the life of me, I just can't figure it out. Here is what I have tried to do so far:
def ldbar():
print "Load: 1%"
time.sleep(0.5)
os.system('clear')
print "Load: 2%"
time.sleep(0.5)
os.system('clear')
print "Load: 3%"
time.sleep(0.5)
os.system('clear')
print "Load: 4%"
time.sleep(0.5)
os.system('clear')
print "Load: 5%"
#So on and so forth up to 100%
ldbar()
So, like I said, is there anyway I can make this shorter?
This should work:
def ldbar():
for i in range(1, 100):
print "Load: {}%\r".format(i),
sys.stdout.flush()
time.sleep(0.5)
ldbar()
It uses a for loop to avoid having the same code over and over again. In the print statement I use \r which moves the cursor to the front of the line, allowing it to be overwriten which is why sys.stdout.flush is used to make sure the output is printed without a newline (notice the comma at the end of the print statement which says that a newline should not be printed).
For Python 3 you would use this (but I think you're using python 2):
def ldbar():
for i in range(1, 100):
print("Load: {}%\r".format(i), end="")
sys.stdout.flush()
time.sleep(0.5)
ldbar()
Here's a nice version using a context manager:
from contextlib import contextmanager
import sys
#contextmanager
def scoped_progress_bar(num_steps, message):
class Stepper(object):
'''
Helper class that does the work of actually advancing the progress bar message
'''
def __init__(self, num_steps, message):
self.current_step = 0.0
self.num_steps = num_steps
self.message = message
def step(self, steps = 1.0):
self.current_step += steps
sys.stdout.write('\r{}:{}%'.format(message, (self.current_step/self.num_steps)*100))
sys.stdout.flush()
stepper = Stepper(num_steps, message) # This is where we actually create the progress bar
yield stepper.step # This is where we do the yield secret sauce to let the user step the bar.
# Finally when the context block exits we wind up back here, and advance the bar to 100% if we need to
if stepper.current_step < stepper.num_steps:
stepper.step(stepper.num_steps - stepper.current_step)
The advantage of this method is that
You can specify an arbitrary number of steps
You can step an arbitrary number of steps
Even if you don't hit the end of the number of steps, the context manager will always print 100% at the end
You can specify an arbitrary message
Usage:
with scoped_progress_bar(10, 'loading') as step:
for i in xrange(7):
step()
time.sleep(0.5)
Which prints:
loading: 10%
loading: 20%
...
loading: 70%
loading: 100%
It's likely a bit overkill for your situation, but thought I'd provide it just in case.
An important thing to note with all of these answers is that they assume you won't be printing out stuff during the process in which you're advancing the progress bar. Doing so will still work just fine, it just might not look like what you expect.
First off, use the Progressbar module (https://pypi.python.org/pypi/progressbar), it already does everything you'll ever want from a text-mode progress bar, and then some.
Now for a fix for your specific implementation, what you want to do is write a bar to stdout (or stderr) with no line return, then erase it, then draw it again. You do it like so:
import sys
import time
sys.stdout.write("0%")
# stdout is line-buffered and you didn't print a newline,
# so nothing will show up unless you explicitly call flush.
sys.stdout.flush()
time.sleep(2)
# Move the cursor back to the beginning of the line
# and overwrite the characters.
sys.stdout.write("\r1%")
sys.stdout.flush()
time.sleep(2)
sys.stdout.write("\r2%")
sys.stdout.flush()
# etc.
But really, use progressbar.

exit loop on keypress in either Shell/Terminal or variable change

So i'm on my Raspberry pi, and i'm wanting to check to see if a sensor has been activated. I'm potentially running off two different things though, one from a shell that i remote into, and the other off a LCD screen with a couple buttons directly connected to the RPi. I think the best way to do this is to run a loop to see if the user press a key (like the enter key or something) OR if the LCD interface has selected to go on. i'd like to run a loop that check if the user has pressed a key or if a variable has changed somewhere denoting the LCD interface has been changed and then move on with my code, but i don't know the best way to do this. Currently, this is what i have:
import thread
import time
import globals #this is where i keep my project wide global variables. it this this is a good way to do it....
try:
from msvcrt import getch
except ImportError:
def getch():
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
char = None
def keypress():
global char
char = getch()
thread.start_new_thread(keypress, ())
globals.LCDVar = 0
while True:
if char is not None:
print "Key pressed is " + char
break
if globals.LCDVar == 1
break
time.sleep(.1)
print "Program is running\n"
<Run whatever code next>
This works, but i'm not sure what happens to the thread that's created. Does the thread stay alive after i press a key? I if change the variable instead of pressing a key, won't the thread still be there? I would feel comfortable with this if the event would only happen once or twice, but this keypress/varble check might happen 100s or 1000s of times and i don't want to keep starting new threads over and over again.
Is there a better way to do this?
Thanks for any advice!!
The thread you created exits when the function called in start_new_thread returns, so you don't need to worry about it running forever (that's from the official documentation on thread.start_new_thread).
As far as the "best" way to do this, I think that reversing what is run in a separate thread and what is run in the main line of execution would be helpful. That is, do work in a separate thread and wait for a key press in the main thread. That's traditionally how functionality like this is implemented and it reduces some of the complexity of your loop.

Python: how to modify/edit the string printed to screen and read it back?

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

Categories

Resources