How do I take keyboard input without a while loop? - python

I know how to get key input directly with the keyboard module but it needs a while loop around it specifically. if I use this in my code obviously it stops it in its tracks!
while True:
event = keyboard.read_event()
if event.event_type == keyboard.KEY_DOWN:
print(event.name)

Use a keyboard.listener in a non-blocking fashion (not in a with statement), as per the documentation:
from pynput import keyboard
def on_press(key):
try:
print('alphanumeric key {0} pressed'.format(
key.char))
except AttributeError:
print('special key {0} pressed'.format(
key))
def on_release(key):
print('{0} released'.format(
key))
if key == keyboard.Key.esc:
# Stop listener
return False
# ...or, in a non-blocking fashion:
listener = keyboard.Listener(
on_press=on_press,
on_release=on_release)
listener.start()
# execution immediately continues past listener.start()
When using the non-blocking version above, the current thread will continue executing. This might be necessary when integrating with other GUI frameworks that incorporate a main-loop, but when run from a script, this will cause the program to terminate immediately.

Related

GlobalHotKeys pynput not working not reacting to function keys

community. I'm trying to put together a quick hotkey script in python here.
For some reason it doesn't react to function keys, meaning the expression '<ctrl>+<F2>': function_1 doesn't work.
I was not able to find any clues in the official documentation or other examples online. Any thoughts?
Here is the script for testing.
from pynput import keyboard
def function_1():
print('Function 1 activated')
def function_2():
print('Function 2 activated')
with keyboard.GlobalHotKeys({
'<ctrl>+<F2>': function_1,
'<ctrl>+t': function_2}) as h:
h.join()
You could try setting up a generic handler to see what events are generated:
from pynput import keyboard
from pynput.keyboard import Controller
keyboard_controller = Controller()
# The event listener will be running in this block
with keyboard.Events() as events:
for event in events:
if event.key == keyboard.Key.esc:
break
else:
print(f"Received event {event}")
Running this script will then print the key press events, pressing Esc will exit the script:
Received event Press(key='a')
Received event Release(key='a')
Received event Press(key=Key.media_volume_up)
Received event Release(key=Key.media_volume_up)
Received event Press(key=Key.f3)
Received event Release(key=Key.f3)
Received event Press(key=Key.ctrl)
Received event Press(key=Key.media_volume_up)
Received event Release(key=Key.media_volume_up)
Received event Release(key=Key.ctrl)
Note that this won't handle hot keys and I don't see some key presses, e.g. to get Key.media_volume_up events I press Fn + F3.
You could potentially use keyboard.Events() handling to trigger your own hotkey equivalent.
I solved this issue by moving away from pynput Global Hotkeys and using just keyboard instead. I still don't understand the nature of this issue with global hotkeys not recognizing F1 key..
Here is the solution I used. I also added the passthrough of values with lambda function for each hotkey.
import keyboard
def func1(key):
print("F1 is pressed with value: ", key)
def func2(key):
print("F2 is pressed with value: ", key)
# define hotkeys
keyboard.add_hotkey('F1', lambda: func1("value1"))
keyboard.add_hotkey('F2', lambda: func2("value2"))
# run the program until 'F12' is pressed
keyboard.wait('F12')

The controller.type method does not work correctly inside the onpress event - Pynput

I'm building a rudimentary autocomplete for my work, and it was doing fine, until I ran into this problem.
See example
import threading
from pynput import keyboard
from pynput.keyboard import Controller
controll = Controller()
def on_press(key):
if(key == keyboard.Key.alt_l):
controll.type("RIGHT !!!")
if(key == keyboard.Key.delete or key == keyboard.Key.enter):
return False
def handleKey():
x = input('INPUT> ')
print(x)
handle = threading.Thread(target=handleKey)
handle.start()
controll.type("RIGHT !!!")
with keyboard.Listener(
on_press=on_press) as listener:
listener.join()
When you press alt, you will see the output with the wrong string;
It must be [2] RIGHT !, but it was pressed [2]ight
But note that this only happens with the method call inside the event.
Is this a known issue? Am I using it wrong? I'm really confused
I believe that only with python3 and pip3 install pynput, this will make the example work
Tested with Windows 10
I've had a similar issue when trying to press a key while listening to a key. By my experience it is next to impossible. I have even tried to use other libraries' press functions such as pyautogui and keyboard inside of the pynput on_press function.
def on_press(key):
if key = keyboard.Key.esc:
return False
else:
if len(words) > i:
# p = keyboard.KeyCode.from_char(words[i])
pyautogui.press(word[i]) // also used keyboard.press(word[i])
i += 1
else:
keyboard.Controller.press(keyboard.Key.space)
with keyboard.Listener(on_press=on_press, suppress=True) as listener:
listener.join()
I feel like since the press action and the listen action are in different threads the program has to finish one before the other. At least that is what I gathered as I also have not found a solution to this. I also tried putting the press line after the listener.join() like this:
def on_press(key):
if key != keyboard.Key.esc:
return True
with keyboard.Listener(on_press=on_press, suppress=True) as listener:
listener.join()
if len(words) > i:
# p = keyboard.KeyCode.from_char(words[i])
pyautogui.press(words[i])
i += 1
else:
keyboard.Controller.press(keyboard.Key.space)
However, this did not solve the issue either because the program finished after listening to one input and then pressing words[i] once.
Please let me know if you find something. However I may not be able to comment bc of low reputation.
I had a similar issue as well, and something like this worked for me (on Ubuntu 20.04):
from pynput import keyboard
import threading
from time import sleep
event_queue = []
event_to_callback = {
# put your own key-to-function mapping here
# keyboard.KeyCode.from_char('c') : deal_with_c
}
def process_events():
while True:
for c in event_queue:
event_to_callback[c]()
event_queue.clear()
sleep(0.2)
keyboard.Listener(on_press=lambda k: event_queue.append(k)).start()
threading.Thread(target=process_events, args=()).start()
This decouples the listener's thread from the controller's thread; any callbacks that invoke pynput.keyboard.Controller methods will now be executed on a separate thread, different from the one of the listener.

Building a key logger with Pynput (saving it to notepad)

Trying to build a key logger with pynput.
Took a look at Here, everything worked fine but not what I wanted, I wanted to save the output to a text file at the same time that the programs running.
I tried sys.stdout but it just dosent save it to the LogTXT.txt file.
Anyways, here's the code:
from pynput.keyboard import Key, Listener
import os
import atexit
import sys
file = os.open(r"C:\Users\USERNAME\Desktop\LogTXT.txt", os.O_RDWR|os.O_CREAT )
def on_press(key):
print('{0} pressed'.format(
key))
def on_release(key):
print('{0} release'.format(
key))
if key == Key.esc:
# Stop listener
return False
# Collect events until released
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
sys.stdout = file
Try to use another way to do this instead of use the stdout,think it as another way:
from pynput.keyboard import Key, Listener
import time
fp = open(r"LogTXT_{}.txt".format(time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())),"w") # open the file
def on_press(key):
print('{0} pressed'.format(key))
fp.write('{} pressed at time:{}\n\n'.format(key,time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()))) # write it.
def on_release(key):
print('{0} release'.format(key))
fp.write('{} release at time:{}\n\n'.format(key,time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())))
if key == Key.esc:
fp.write("End Press") # press esc.Exit the script
fp.close()
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
Some example of output in the file:

pynput keylogger does not write to file

I have a problem with this keylogger program. It must write to a file but it doesn't do that. What is wrong?
The program must listen to the keyboard and write it to a file (before, there is a check if the file exists or not). But it doesn't write to the file, it only creates the file.
from pynput import keyboard
import os
if os.path.exists("prova3.txt") == True:
f = open("prova3.txt","a")
else:
f = open("prova3.txt","x")
def on_press(key):
try:
f.writelines("///key [ {0} ] pressed ///".format(
key.char))
except AttributeError:
f.writelines("///special key {0} pressed///".format(
key))
def on_release(key):
f.writelines(["///key [ {0} ] released ///".format(
key)])
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()
First of all, make sure that you took note of the warning from the pynput docs:
Starting a keyboard listener may be subject to some restrictions on your platform.
If you are on a Mac like me, then you have to do this:
The process must run as root.
Your application must be white listed under Enable access for assistive devices.
For that second item, check these steps from a related SO post.
Now, for the actual file writing problem, one solution is to call f.flush() after calling f.writelines to ensure that data is actually written to the file. (see this related post for some explanation: what exactly the python's file.flush() is doing?). I am not familiar with pynput's underlying implementation, but the docs says it uses threads and that seems to affect File I/O. It's also good practice to call f.close() when you're done with the file.
from pynput import keyboard
if os.path.exists("prova3.txt"):
f = open("prova3.txt", "a")
else:
f = open("prova3.txt", "x")
def on_press(key):
try:
f.writelines("///key [ {0} ] pressed ///".format(
key.char))
except AttributeError:
f.writelines("///special key {0} pressed///".format(
key))
f.flush()
def on_release(key):
f.writelines(["///key [ {0} ] released ///".format(
key)])
f.flush()
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()
f.close()
An alternative and a better practice is to use a with statement when writing to the file. That puts f and the pynput listener all in the same context. With this way, calling flush is not needed (but you still can if you want to).
from pynput import keyboard
with open("prova3.txt", "a") as f:
def on_press(key):
try:
f.writelines("///key [ {0} ] pressed ///".format(
key.char))
except AttributeError:
f.writelines("///special key {0} pressed///".format(
key))
def on_release(key):
f.writelines(["///key [ {0} ] released ///".format(
key)])
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()

Detecting a keypress in python while in the background

I am trying to find a way to detect a keypress and then run a method depending on what key it is.
I can already do this with Tkinter. But what I can't do is detect the keypress while the window is in the background. I will be running this program in the background while I play a game. I need it to be able to detect inputs while I'm in the game.
Is there any way I can do this with Tkinter or something else? Preferably I would like to not have to download anything external as I would like to distribute this to some other people.
pyHook seems like it would work well for this (mentioned by furas)
from pyHook import HookManager
from win32gui import PumpMessages, PostQuitMessage
class Keystroke_Watcher(object):
def __init__(self):
self.hm = HookManager()
self.hm.KeyDown = self.on_keyboard_event
self.hm.HookKeyboard()
def on_keyboard_event(self, event):
try:
if event.KeyID == keycode_youre_looking_for:
self.your_method()
finally:
return True
def your_method(self):
pass
def shutdown(self):
PostQuitMessage(0)
self.hm.UnhookKeyboard()
watcher = Keystroke_Watcher()
PumpMessages()
I suggest not to use pyHook anymore, because it's an old, not maintained library (last updated in 2008)
Alternatively there are many other libraries, which are actively maintained for example pynput:
from pynput.keyboard import Key, Listener
def on_press(key):
print('{0} pressed'.format(key))
def on_release(key):
print('{0} release'.format(key))
if key == Key.esc:
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
keyboard can be an another alternative which may be worth considering.
Too get all the properties from your key press event. you can do the following
import pythoncom, pyHook
def OnKeyboardEvent(event):
print('MessageName:',event.MessageName)
print('Message:',event.Message)
print('Time:',event.Time)
print('Window:',event.Window)
print('WindowName:',event.WindowName)
print('Ascii:', event.Ascii, chr(event.Ascii))
print('Key:', event.Key)
print('KeyID:', event.KeyID)
print('ScanCode:', event.ScanCode)
print('Extended:', event.Extended)
print('Injected:', event.Injected)
print('Alt', event.Alt)
print('Transition', event.Transition)
print('---')
# return True to pass the event to other handlers
return True
# create a hook manager
hm = pyHook.HookManager()
# watch for all mouse events
hm.KeyDown = OnKeyboardEvent
# set the hook
hm.HookKeyboard()
# wait forever
pythoncom.PumpMessages()
Now know all the details of the key press and do operation on top of this.
pressing 's' would look like this
MessageName: key down
Message: 256
Time: 449145375
Window: 2558060
WindowName: "file name"
Ascii: 115 s
Key: S
KeyID: 83
ScanCode: 31
Extended: 0
Injected: 0
Alt 0
Transition 0
See pynput.
def on_release(key):
global running
if key == keyboard.Key.esc:
running = False;
running = True
listener = keyboard.Listener(on_release=on_release)
# run listener in background so that the while loop gets executed
listener.start()
while running
print("running some code")
listener.stop()

Categories