Capturing key presses from the terminal - python

I am trying to write a program that captures at any time which key is being pressed. The ultimate goal is to control a robot from the keyboard, e.g. using wasd keys to control movement. It's rather easy using pygame, but as I want to be able to access my robot over SSH, I am looking for a purely bash-based solution. The curses library seems to be the way to go (but please let me know if there is a better solution). I now have something like this:
import curses
def main(screen):
screen.timeout(50)
key = ''
while key != 'q':
try:
key = screen.getkey()
screen.addstr(0, 0, 'key: {:<10}'.format(key))
except:
screen.addstr(0, 0, 'key: {:<10}'.format('N/A'))
if __name__ == '__main__':
curses.wrapper(main)
which largely works fine, except for some unexpected behaviour: when I press and hold a key, it very briefly (and only once) alternates between the key in question and the 'N/A' fallback, as if I press and release and then press and hold.
I think my issue is caused by the character repeat delay setting on my machine. Indeed, if I increase the curses timeout setting to half a second, the problem is 'solved', but I want my program to be more responsive than half a second. Any way I can overrule the character repeat delay within my program? Or alternative solutions?
Note: Not sure if this is relevant, but I am using bash on Windows 10 (WSL) for testing purposes, and would ultimately like to run it on Raspbian.

Related

How to hold an keyboard key using pynput/pyautogui [duplicate]

I'm currently working on a script that presses the 'w,a,s,d' keys in order to move a character in any game.
For this to work, i need to have the 'w' key pressed for a specific amount of time. How can I achieve this?
I thought of something like:
pyautogui.keyDown('w')
time.sleep(2)
pyautogui.keyUp('w')
But this just pauses the whole program and no key is being pressed so this has no use to me.
As said in the doc-string from pyautogui.keyDown():
Performs a keyboard key press without the release. This will put that
key in a held down state.
NOTE: For some reason, this does not seem to cause key repeats like would
happen if a keyboard key was held down on a text field.
You need a different approach - you can may use pygame - with this
Or, if you want to stay with pyautogui you can try something like this:
def hold_W (hold_time):
import time, pyautogui
start = time.time()
while time.time() - start < hold_time:
pyautogui.press('w')
with pyautogui.hold(key):
pyautogui.sleep(hold)
This will do the trick without making your own function.

How can I block specific keyboard keys using python's keyboard module?

I've been trying to make a program on Windows 10 that stops people using my screen when I'm away from my screen. I know there is the "Lock" option in the start menu, but my company blocks the use of that for some reason.
I've used tkinter to make a black screen in full screen that can't be taken out of full screen unless you enter a password or insert a physical security key.
But I realise that people, upon seeing this, would just press the windows key and close the window.
So I want to try and make it so that I can temporarily disable specific keys, like ALT, DEL, CTRL and all the function keys so that it can't be closed using the keyboard shortcuts. I also realise that people could just shut down my PC using the physical switch. I do want to keep all of the keys for letters, numbers, symbols and caps lock enabled so that the user can type the password.
I know of ways to do this with keyboard.block_key(), but I do not know what values to put into the parentheses to block only CTRL, ALT, DEL, SHIFT, the Windows key, ESC and all of the function keys. I'm not sure if it is an integer I put in for a key code / ID or if I put in a string.
If anyone could help, that would be greatly appreciated!
I don't think that its possible to block specific keys using a python program.
There is a different approach to this, you may be able to run a time.sleep function to temporarily disable the keyboard.
import ctypes
from ctypes
import wintypes
BlockInput = ctypes.windll.user32.BlockInput
BlockInput.argtypes = [wintypes.BOOL]
BlockInput.restype = wintypes.BOOL
blocked = BlockInput(True)
if blocked:
try:
pass # do something
finally:
unblocked = BlockInput(False) # unblock in any
case
else :
raise RuntimeError('Input is already blocked by another thread!')
Above is an example program that I wrote for blocking keys on windows. Instead of the pass functions, you could replace that with code for what you want to do. (Example: wait unit your physical security key is entered)
This is a differential approach, and is easier to add to later on:
import time
class keyboardDisable():
def start(self):
self.on = True
def stop(self):
self.on = False
def __call__(self):
while self.on:
msvcrt.getwch()
def __init__(self):
self.on = False
import msvcrt
disable = keyboardDisable()
disable.start()
time.sleep(10)
disable.stop()
If this is really that big of a problem at work, I would recommend setting up a key logger to see if someone really is messing with your computer and report it to your boss.

Single key detection to work with the possibility of adding long inputs

I have been facing this problem for the last week,I thought it would be trivial but after trying many different approaches I don't know what else to try.
I have an application where I need to have key detection (to move a robot arm with the keyboard) but when I press enter I need to add some inputs, which should be as long as I want, just some normal input("insert here").
I know about the python libraries to get key detection, I got pynput to work successfully but it crashes my raspberry pi when I start and stop the threads a few times,I tried the Keyboard library but the whole root requirement is a let down, I also got curses to work and this seems to be solid and is (almost) not causing any issues, so detecting 1 key is not a problem.
I of course know how to name my files and get all the information that I need by doing input(), so if I had to use one of those options the job would be rather simple, the challenge comes when I try to apply both approaches together, basically detect the keys to do everything I need, and use python Input to get all the inputs from the user as soon as enter is pressed, all the libraries to detect key seems to take full control and they don't want to release it without a fight. They seem to expect the user to always require single key detection but in my case I would need to constantly turn it on and off, I couldn't figure out any efficient (or not) way to get it to work properly.
My question is:
What is the best approach to have key detection + full user input when needed with curses (or any alternative) in a non blocky way (as my code need to do some other things while listening for keys), is creating and destroying the whole thing the only alternative?
This is my current test code that I created for simplicity (which works but blocks everything while listening for keys):
import curses
import time
import os
stdscr = None
addInput = False
def SetupCurses():
global stdscr
stdscr = curses.initscr()
curses.cbreak()
stdscr.keypad(1)
def StartCurse():
global addInput
key = ''
while key != ord('q'):
key = stdscr.getch()
stdscr.addstr(str(key))
if key == ord('a'):
print("\nyou pressed a\n")
if key == 10:
print("\nyou pressed enter!\n")
addInput = True
break
def EndCurse():
curses.endwin()
while(True):
SetupCurses()
StartCurse()
EndCurse()
if addInput:
theinput = input("add your input\n")
print(theinput)
time.sleep(4)
addInput = False
#if there isn't any input to add I want the code to continue because there is non-related keys stuff to do, but of course it stopped at "StartCurse"
#if there is something to add the code can stop at addInput
The reason for the loop is because the user can save as many positions as he want, so after adding some inputs the possibility of adding more is there.
I saw people making this non-blocking by closing the curses loop after a few seconds (which stops everything anyway...) kind of getting the input by luck...something like:
def ExecuteCurses():
global AddInput
#open it and close it very quickly to grab a key if it is pressed
c = stdscr.getch()
if c == ord('a'):
print("you pressed a")
AddInput = True
time.sleep(1)
curses.endwin()
If you want a full and long user input you will need to use the curses.echo() and then use the stdscr.getstr(). That will wait for the user to press enter().
And to not block the program while getting input you need threading which you will have to import at the top of your program
And for the threading here is a link so you can find out more about threading.
I hope it answers your question

Getting input in python via the same window?

I would like to know how to get user input in python without using the command line or an input box.
Let me explain. I do not want to do this
#All code is python 3
name=input("What is your name?")
Why? When running scripts, the command line is not auto-focused. Furthermore, it pops up another window, something I do not want because I can't hit escape to close it in a hurry (Something which you may want to do if you're playing a game).
What have I tried?
I looked at WX and it's dialog function, something like this:
import wx
app=wx.App()
def text_entry(title,message):
result=None
dlg=wx.TextEntryDialog(None, message,title)
if dlg.ShowModal()==wx.ID_OK: result=dlg.GetValue()
dlg.Destroy()
return result
text_entry("Text entry","Enter something here")
While this works, it pops up another window which again, I do not want. However, it is closer to what I am ultimately looking for, because I can hit escape to make it go away.
I have tried using pygame and it's key.get_pressed() function, but it inserts a lot of the same letter into the entry, even if I gently tap the key. Also, when I implemented it into the project, it can only pick up on normal letters. Writing 26 if statements to detect key presses for a single letter with or without the shift key seems a little counter intuitive.
Finally, I am a bit hesitant to try tkinter. I happen to be blind, and from what I read, tk is very visual, which makes me concerned that it won't play nicely with my screen reader (NVDA).
So, I'm here. After searching on google for "getting input without using command line in python 3", "input in the same window", and "input without using input()" yielded nothing.
To recap, I want to accept user input without using the input() function, and without any additional windows popping up for the duration of me doing so.
Thank you.
What about this solution using the msvcrt module. At any time if you press escape then the program will exit. Python sys.exit(), and built-ins exit() and quit() all call raise SystemExit so this is just one less call to perform. If you press the enter or return key then the while loop ends and you can use the keys that were pressed later in your program as they are stored in the variable user_input. The print at the end just proves that the pressed keys are stored in user_input variable and the input() function simply to leave the window open so you can see it working.
import msvcrt
user_input = b''
while True:
pressed_key = msvcrt.getche() # getch() will not echo key to window if that is what you want
if pressed_key == b'\x1b': # b'\x1b' is escape
raise SystemExit
elif pressed_key == b'\r': # b'\r' is enter or return
break
else:
user_input += pressed_key
print('\n' + user_input.decode('utf-8')) # this just shows you that user_input variable can be used now somewhere else in your code
input() # input just leaves the window open so you can see before it exits you may want to remove
So after doing some more research, I found this:
https://codeload.github.com/Nearoo/pygame-text-input/zip/master
I think this is what I am looking for, though it still needs to be slightly modified. Thank you for the assistance

OSX Controlling the keyboard with Python: hold duration

I'm trying to get Python 3.5 to control the keyboard on OS X, using some code I found using Quartz Event Taps.
def KeyPress(k):
keyCode, shiftKey = toKeyCode(k)
CGEventPost(kCGSessionEventTap, CGEventCreateKeyboardEvent(None, keyCode,True))
time.sleep(3.0001)
CGEventPost(kCGSessionEventTap, CGEventCreateKeyboardEvent(None,keyCode,False))
So you would think increasing the x in time.sleep(x) should increase how long the key is held down for, but it does not seem to be working. I have another bit of code that will wait 4 seconds for me to activate another application, like Terminal or TextEdit, and then calls KeyPress('s') to hold down the s key. It prints the s and pauses, but if I actually hold the key down, it would continue to print sssss. So it does not seem to be actually holding the key down, as I would like.

Categories