Python (preferably 3) equivalent to INKEY$ - python

I am using Python 3.6.4 on a Windows 7 system (I have other systems like Win 10 and Android but this is my starting point).
INKEY$, for those not familiar with BASIC (pretty much any flavor), is a function that checks the keyboard buffer, returning that data as a string or a null/empty value ("") if there was no data, and clears the buffer. The length of the returned string depends on the data in the buffer, normally 0, 1, or 2 on a single keystroke (a fast typist could fill the small buffer between checks in the old days). The Enter key was not needed (unless that was what you were looking for) or processed and the program did not pause unless programmed to do so.
Pauser:
a$=""
while a$=""
a$=inkey$
wend
Flow Interrupter:
a=0
while a < 1000
a=a+1
print a
a$=inkey$
if a$<>"" then exit while
wend
Quick parser:
a$=inkey$
if a$<>"" then
rem process input
rem like arrow keys/a w s z for directional movement
rem useful for games and custom editors
end if
I am wanting to know if Python has a simple cross platform function (ie not 10+ lines of code unless in an importable module/class) that is the equivalent to the INKEY$ function? Also, I am not wanting to import the gaming module(s), just want an INKEY$ function equivalent (simple, straight forth, small).
import inkey
a = inkey.inkey()
Update #1:
After I installed the readchar module and corrected a reported error by Python (stdout.write(a) needs to be stdout.write(str(a)) as the variable 'a' appears to be returned as a byte string from readchar() function) when using the code listed by Mr. Stratton below, I only get continuous stream of b'\xff' and console echoed characters if there where any keypresses.
Stripping it down to use only the function doesn't help either:
from readchar import readchar
from sys import stdout
import os
#I like clear screens, and I can not lie
os.system('cls') # For Windows
#os.system('clear') # For Linux/OS X
def inkey():
"INKEY$ function"
return readchar()
#let the processing hit the floor, elsewhere
b=0
step = 1
while b < 1000:
b = b + step
print(b)
#convert bytes to integers
a = int.from_bytes(inkey(), "big")
#remember I keep getting b'\xff' (255) when buffer is empty
if chr(a) == "a":
step = step - 1
a = 255 #don't stop
if chr(a) == "l":
step = step + 1
a = 255 #don't stop
if a != 255:
break
It is supposed to count b from 0 to 999, stopping on almost any keypress, 'a' decreases the step, 'l' increases it. Instead, it prints the keypress either before or after the value of b depending on timing and continues until b = 1000. Nothing I did made a difference.
While the Pauser function can be replaced with an input() (i = input("Press Enter key to continue")) the other two variants can't be changed so easily it seems.

The closest to what you’re looking for is probably the readchar library.
Here’s an example that resembles the old BASIC logic:
from readchar import readchar
from sys import stdout
a = ' '
while ord(a) not in [3,24,27]:
a = readchar()
stdout.write(a)
stdout.flush()
if ord(a) == 13:
stdout.write("\n")
Those numbers that break the loop are CTRL-C, CTRL-X, and ESC respectively. The number 13 is the carriage return; the example writes a line feed following each carriage return to avoid overwriting text.
The equivalant to the old ASC(A$) in BASIC is ord(a) in Python. (And CHR$(A$) is chr(a).)
Note that this will block on reading. If no keypress is waiting, Python will stop on the line a = readchar() until a key is pressed. To get the full effect of BASIC’s INKEY$, you’ll need to verify that some data is waiting before reading it. You can do this using Python’s select library.
from readchar import readchar
from sys import stdin, stdout
from select import select
def inkey():
if select([stdin,],[],[],0.0)[0]:
return readchar()
return ''
a = ''
timer = 0
while a != 'Q':
a = inkey()
if a != '':
stdout.write(a)
stdout.flush()
if ord(a) == 13:
stdout.write("\n")
timer += 1
if timer > 1000000:
print("Type faster, human!")
timer = 0
This defines an inkey function and returns the empty string if nothing is waiting. If something is reading, it returns readchar(). Every 1,000,000 times through the loop, it tells the human on the other end to type faster.
In this version, it quits on a capital letter “Q”, as this does not block CTRL-C, CTRL-X, and ESC from breaking out of the program altogether.
This may have trouble if you’re using Windows, as select.select at least at one time did not work on Windows. I have no means of testing that, however.
You may also want to look at pynput if you don’t need it to look exactly like BASIC. The canonical means to do this in Python is probably to set up an event that calls a function or method. Your script goes on doing its thing, and if that function (or method) is invoked, it cancels or modifies the action of the main loop.

Related

How Do You Make Timed Keyboard Responds?

I want to make it so that the game will print and ask to type a certain letter, and if I don't then I lose
What I want it to be like:
Press E:
(if you don't type it after 5 seconds, you lose. If you DO type it, the game will continue)
Update: I have been told that this is a bad answer, so I decided to improve it, but I left in my old reply for you to still see
Old reply:
Check out the keyboard library, and probably the time library too. I think the easiest way to do this is to create some sort of clock, save a "start time" (what time the prompt came up) and then enter a loop, exiting only when you have either pressed the key, or 5 seconds from the start time has elapsed. Make sure you keep track of which one actually happened.
My updated response, including a code example:
Okay so we're going to be using the time and keyboard libraries.
Note: In order to use most functions in keyboard, you need to run your script on root. You'll have to look up instructions if you're running a different OS, but what I did on my linux machine was just first installing keyboard on root (idk why it needed to be installed seprately but it did) with sudo pip install keyboard, and then running my code as sudo python3 filename.py
.
Our code is going to be running all within a while loop. Here's the basic idea:
play = True
while play:
play = runGame('e')
where runGame() is a func that plays 1 "round" of the game, and returns True if you win, False if you lose.
First, let's look at a way to actually tell if we pressed some desired key (like 'e') We'll be using keyboard.on_press_key() We'll be passing 2 arguments into it, key (what key will trigger it) and callback (a function to call when that key gets pressed) see the docs for more info.
So something like this:
import keyboard
def keyIsPressed(x):
global buttonPress
buttonPress = True
# calls keyIsPressed() when we press targetKey
keyboard.on_press_key(targetKey, keyIsPressed)
i added the unused x argument in the function because on_press_key wants to return some data into the function when you use it. Feel free to print(x) to get a look at what that data is. I just did that as an easy way to deal with an error.
Okay, so let's put everything together finally:
import keyboard
import time
#func called by on_press_key when our targetKey is pressed
def keyIsPressed(x):
global buttonPress
buttonPress = True
def runGame(targetKey):
# variable to tell if we have pressed the targetKey yet
global buttonPress
# always start the round false
buttonPress = False
# calls keyIsPressed() when we press targetKey
keyboard.on_press_key(targetKey, keyIsPressed)
#start time
st = time.time()
while True:
#current time
ct = time.time()
# if 5 seconds or more has passed between start time and current time
if ct >= (st + 5):
print("you took too long!")
return buttonPress #False
#if you have pressed the button
elif buttonPress:
#unhook the key now that you're done with it
keyboard.unhook_key(targetKey)
print("\n\nwow u did it good job!\n\n")
return buttonPress #True
# Run
buttonPress = False
play = True
# you don't have to use a list, but I'm doing so for my prototype
letters = ['e', 'f', 'g']
#the amount of times you've played
loops = 0
while play:
# play the game, use 'e', then 'f', then 'g'
# outside of a prototype, you'll need to update the list or use a different
# method to avoid errors after 3 rounds
play = runGame(letters[loops])
#up loop counter
loops += 1
#remove all key hooks when you are done playing
keyboard.unhook_all()
Hopefully that's comprehensive enough without being too wordy. I really wasn't expecting this to be as difficult as it was, but all of the intuitive solutions that I thought of actually ran into issues either with dropping inputs, or freezing the loop. So this is the simplest implementation I could come up with.

Whole program typing effect

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

Cancel input when escape is pressed

I would like to have a function that works like input(), but that returns None (for example) when Esc key is pressed.
I tried doing the following:
def input(t):
print(t)
text = ""
while True:
char = msvcrt.getche();
if char == chr(27).encode():
return None
if char == b'\r':
return text
try:
text += char.decode()
except:
pass
This works for the most part, but the problem is that backspace won't work, and that I get strange characters when pressing non-character keys like the arrow keys, or the combination Ctrl+C. So getche might not be the answer to my problem.
I was under the impression that this would be a common problem, but I haven't been able to find a solution.
I was under the impression that this would be a common problem
No it is not. There are two common idioms when it comes to io. First is called line oriented. It can be used in batch mode programs where the standard input can be redirected to a file or a pipe. In this mode, the program itself should not even try to know whether it reads from a real keyboard or from a file (well more or less, password input being the exception). The low level driver, or the input library can add some goodies like in line edition (backspace processing), but this is not directly accessible to the programmer, and in any case is not intended to be portable.
Second mode is for GUI. Here, most of the things are under programmer control: the position of input fields on the screen, their size, and optionally the processing of special characters. More exactly, it is generally processed through a graphic library like tkinter (tk), pyqt or pyside (Qt), or ...
What you ask for is a medium way. It is common for beginners to try to explore it, but after some time, we all realize that for real world programs the two common idioms are enough.
You may find this answer more philosophical that technical, and in theory there would be nothing bad in improving line edition, but in real programs, I have never found any true reason to go beyond what is currently available.
I made a code using module keyboard. You an install it using pip install keyboard. It reads left,right,end,home,enter,escape,backspace and many other keys.
You can use function read_input. It has two options text and cancel. text is the text to print and cancel is the key which will cancel the input. If you press escape then it returns None, if enter then typed text.
Here is the code:
def read_input(text="",cancel="esc"):
import keyboard,string as t
print(text,end="")
output = []
output2 = []
allowed = t.ascii_letters+"1234567890!##$%^&*()-=_+{}[]|\:;',<>./?`~"+'"'
while True:
key = keyboard.read_event()
k = key.name
if key.event_type == "up":continue
if k == cancel:print("");return None
elif k == "enter":break
elif k == "end":output = output+output2;output2 = []
elif k == "home":output2 = output+output2;output = []
elif k == "left":
try:output2.insert(0,output.pop())
except:pass
elif k == "right":
try:output.append(output2.pop(0))
except:pass
elif k == "space":k = " ";output.append(k)
elif k == "backspace":output = output[:-1]
elif k in allowed:output.append(k)
foutput2 = ""
for put in output:
foutput2 += str(put)
for put in output2:
foutput2 += str(put)
for i in range(0,len(foutput2)+2):keyboard.press_and_release("backspace")
print(foutput2)
return foutput2
You can use it like normal input:
print(read_input("Enter your name: "))
It has some issues on the other side.
It read keys from the whole windows, not only python.
The keys typed in first second(when executed) may be ignored.
Here is an animation:

python does not exit program properly

In my program I want to interact with user and ask him to press specific letters to do some things (I think that logic of this game is not relevant for my question). When I start game and as first letter I press 'q' program exit immediately, but when I play for a while (use few times 'g' and 'r') I have to press few times 'q' too before I could exit program (Every time I'm getting the same prompt as on the beginnig of the game "Enter g to start ... ")
I'm using Canopy and Python 2.7.
t_h = ''
def pg(wl):
global t_h
result = raw_input("Enter g to start new game, r to replay last game, or q to end game: ")
possible_choices = ["g", "r", "q"]
if result in possible_choices:
if result == 'g':
t_h = dh(n)
ph(t_h, wl, n)
if result == 'r':
if t_h == '':
print 'You have not played a game yet. Please play a new game first!'
else:
ph(t_h, wl, n)
if result == 'q':
return None
else:
print "Invalid letter."
return pg(wl)
The function pg calls itself recursively on any non possible_choice case (since only q returns directly) -- that is, on the return pg(wl) line.
The situation you describe implies that either one of, or both ph and dh are calling pg again.
This means that for every non q input you will have a prompt for a new question on the stack from ph (or ph and dh) and one from the recursive call to pg. This will cause the exact behaviour you describe, where one q wont suffice to exit. With the code you posted -- that is, without dh and ph -- it is not possible to know precisely, but this is the logical conclusion.
If you want the possibility to exit immediately you would have to use a simple infinite loop with break in case of q instead of recursion.
Another possibility is following #PauloScardine's idea of using exit(), if what you want is really to exit the whole process. Again, with the piece of code you posted, it is not possible to know whether this is possible (pg being called directly from a main function).
It's difficult to tell without seeing more of your code (specifically code for dh and ph), but I'd guess that pg is being called from one of those functions, or some other function in your code.

Handling dependency of functions in python

I want to write a script for automation of sequence of events, where the execution of the next sequence depends upon the success of the previous step. There are basically 8 functions which I want to call onne by one and if one fails, I want to exit at that point. So how can i handle this in python?
Basic if, and elif statements can overcome your needs.
Say you had a function that returned a variable.
def f(z):
data = z
return data
You can analyse the result with a if. Assign a new variable to the function f:
x = f(0)
if x == 0:
#do something
So if x is equal to 0, continue with code. But what is c = 1?
import os
x = f(1)
if c != 0:
os._exit(0)
os._exit(0) quits the program.
I highly recommend taking a look at pytest (or one of the other Python testing frameworks).
This guide should get you up and running quickly:
http://pytest.org/latest/getting-started.html

Categories

Resources