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:
Related
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.
I'm very new to programming as a whole. Had some basic experience with C++ and HTML, like simple calculators and other really basic stuff(I started learning on my own a few months ago) and because of this I'm not even sure if I am posting this question correctly. Maybe it goes without saying but I am new to stackoverflow as well(My first post!), so I may do some things wrong here and I kindly ask for your comprehension. Any constructive criticism is more than welcome!
So here's my problem: I am trying to make a simple chatting program in Python 3, but here's the catch: I want to use python's asychronous capabilities to allow the user to terminate the program at any given time the esc key is pressed. I've looked into a few libraries already, such as pynput, asyncio, tornado and getch(). But no matter how I change my code I just can't seem to reach my goal here.
I made an async function called runalways() that's supposed to run during the entire duration of the program, parallel to the other functions. But it doesn't and I couldn't figure out why...
I'm sorry if I couldn't explain my issue properly. Some terms and concepts are still rather blurry to me, but hopefully it won't be so for long. I'll leave the code I have so far which does a good job implementing the chat function, but that's about it. The async bit is basically inexistent at this moment.
It would be really appreciated if someone could explain what I'm missing:
import time, asyncio
from msvcrt import getch
# Creates a chat-like effect
def typing(text):
for i in text:
print(i, end=" ")
time.sleep(0.1)
time.sleep(1.0)
# Terminates the program
def termination():
print("Program terminated")
time.sleep(0.5)
SystemExit
# Defines async function to read pressed keys and terminate if Esc is pressed
# (Not working)
async def runalways():
while True:
print(ord(getch()))
key = ord(getch())
if key == 27: # Esc
termination()
# Initialize chatting
def startChat():
typing("\nHello!\n")
typing("How are you?\n\n")
typing("Answer:\t")
x = input()
typing("\nAnyways, that isn't relevant right now.\n")
time.sleep(0.5)
typing("Can we get started?\n\n")
typing("Answer(Y/N):\t")
x = input()
# validates input
while (x != "Y") & (x != "N"):
typing("\nHey, don't go fooling around now. Keep it real! Try again.\n\n")
typing("Answer(Y/N):\t")
x = input()
if (x == "Y") | (x == "y"):
typing("\nThere you go! Now, let's go through the basics...\n\n")
termination()
elif (x == "N") | (x == "n"):
typing("\nSorry to hear that... See you next time, then!")
termination()
# Defines starter point for the program
def main():
runalways()
startChat()
main()
I will try my best to clarify any questions that may help the understanding of my issue. Thanks in advance!
You have two functions competing to grab the input. runalways wants to grab them with getch, while your main chat loop is trying with input. The two processes conflict; only one can consume each character. Whenever one thread grabs input, the other gets to claim the buffer for the next input.
Instead, you need to set up a single input channel that terminates immediately on ESC; otherwise, it echoes to the terminal and passes the character to the main loop (or buffers it until EOL appears).
Right now, I'm running the following code in Python 2.7:
import readline as rl
rl.parse_and_bind('set editing-mode vi') #allow for arrow keys to be used
rl.set_completer()
raw_input()
According to this, rl.set_completer() should remove the completer function, which I assumed would make tab work normally. But, the tab key just doesn't work at all.
I've also tried writing my own function and passing it in as a completer, but that didn't work either. (If someone could find a way of doing this that would make the tab key work normally, that would also suffice.)
How do I get the ability to use arrow keys with raw_input, but also have a normal tab?
You can use
#allow for arrow keys to be used for raw_input.
readline.parse_and_bind('set editing-mode vi')
#set the tab key to make 4 spaces
readline.parse_and_bind("TAB: ' '")
For some reason, using readline.parse_and_bind("TAB: '\t'") caused Python to use way too much of the CPU and would just freeze the screen, so I had to switch it to use spaces.
rl.set_completer()
Removes completer function, but not the TAB binding. So tab is eaten, but nothing is done as the tab is passed to a "None function".
readline.parse_and_bind("TAB: '\t'")
results in infinite calls to the underlying completer function.
Read TAB -> Return TAB -> Read TAB …
Simplest method, at least when testing here, is to use the default emacs mode. If one only import readline without setting anything, read_raw works for arrow keys, and TAB result in TAB.
If that is not an option set a custom completer function. (Works the same for Python 3). This is also very likely the safer option.
If one press TAB the completer function is called. For one TAB key press the state argument starts at 0 and increment for each time function is called. As we return TAB it is important that we check that state is 0. Else it will be called again and again.
We also have to return the text, if any, else it is eaten by readline.
Simple example:
#!/usr/bin/env python2.7
import readline
def rl_tab_expander():
def completer(txt, state):
if state == 0:
return txt + '\t'
return completer
readline.set_completer(rl_tab_expander())
readline.parse_and_bind('set editing-mode vi')
while 1:
inp = raw_input("$ ")
if inp.strip() in ('exit', 'q', 'quit'):
break
Check on original test
You could use a log file. Here we look at what happens in the completer function if we return TAB or if we do not check state as per example above. We set a limit for state at 25, else it would continue ad infinity.
Run script in one console window and do a tail -f /tmp/test-readline.log in another.
#!/usr/bin/env python2.7
import readline
import logging
logging.basicConfig(filename = '/tmp/test-readline.log', level = logging.DEBUG)
def rl_tab_expander():
def completer(txt, state):
logging.debug("STATE: %2d TXT: %s", state, repr(txt))
if state > 24:
return None
return txt + '\t'
return completer
readline.set_completer(rl_tab_expander())
readline.parse_and_bind('set editing-mode vi')
while 1:
inp = raw_input("$ ")
if inp.strip() in ('exit', 'q', 'quit'):
break
Result
DEBUG:root:STATE: 0 TXT: ''
DEBUG:root:STATE: 1 TXT: ''
DEBUG:root:STATE: 2 TXT: ''
...
DEBUG:root:STATE: 24 TXT: ''
DEBUG:root:STATE: 25 TXT: ''
Without limiting state level this would have continued until program was killed or if we press Ctrl+C and get lucky.
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.
I'm trying to write a small Python program for use in secondary schools to teach about ciphers.
It's all part of an ongoing project they have to do.
However on my while loops I have an if condition inside them and if the condition isn't met the first time it just infinitely loops until I break.
while esc == False:
if guess != cipher:
print("ERROR: INCORRECT CIPHER:" + Decrypt(guess, enc))
pass
else:
esc = True
print("Correct Cipher! Decryption successful:" + Decrypt(guess, enc))
The cipher here is 12 and if that is input it carries on as normal, however any other input just gives the error message and loops out forever.
I'm from a C#/C++ background usually and I know Python needs to be in line with its tab characters and white space and I've checked that a few times but I'm at a loss now.
ADDITIONAL CODE:
This works fine and It's done the same way
while esc == False:
if CheckPassword(pw) == True:
print("Authorisation successful.")
esc = True
else:
pw = input("Password = : ")
ADDITIONAL PLACES WHERE SAME PROBLEM HAPPENS:
while esc == False:
if attempts < 1:
esc = True
GameOver()
elif x == "USERS":
print("USERS UNAVAILABLE.")
attempts = RemoveAttempt(attempts)
pass
I'm not sure what else you are expecting to happen. If the guess is incorrect once you get into the while loop, it is going to always be incorrect, because you never ask the user for another guess. Note that in the one that does work, the else clause has an input for a new password: you don't do that in either of your non-working examples.
(Also I think you might be confused about what pass does: it's simply a no-op, ie it does nothing at all, and is only useful where you syntactically need a statement but don't want to do anything. That's not the case in either of the places you use it, so you should remove it. Unless you perhaps meant break instead, to break out of the loop?)