Is there any way to detect a keypress that is executed programmatically? - python

I want to detect if a keypress was executed programmatically (not by user's physical press). Is there any way to do this?
import mouse
import keyboard
import time
keyboard.press("a")
if keyboard.is_pressed('a'):
print ("pressed")
I assume 'is_pressed' in the code above only detects actual input from user hence it wouldn't show print ("pressed"). All I could come up with to solve this is the code below which works just fine for my intention but I want to know if there's a better optimized way.
import mouse
import keyboard
import time
keyboard.press("a")
keyboard_a = True
keyboard.press("b")
keyboard_b = True
if keyboard_a:
print ("a pressed")
if keyboard_b:
print ("b pressed")

It is not possible to distinguish between key press events that are triggered programmatically and those that are triggered by user input. The same is true for readchar, msvcrt, or keyboard libraries.
So, the library provides a way to detect and respond to key press events, regardless of their origin. Hence, your approach with a flag is good.
I don't know your precise aim, but maybe you would prefer to use send and a register event like this
import keyboard
import threading
is_programmatic = False
# Define a function to be called when a specific key is pressed
def on_key_press(keyEvent):
global is_programmatic
if keyEvent.name == 'a':
if is_programmatic:
print("Key press event triggered programmatically")
else:
print("Key press event triggered by user input")
is_programmatic = False
# Register listener
keyboard.on_press(on_key_press)
# Start keyboard listener
keyboard.wait()
# or start a thread with the listener (you may want to sleep some seconds to wait the thread)
thread = threading.Thread(target=keyboard.wait)
thread.start()
and to issue the event
is_programmatic = True
keyboard.send("a")

You can create a list and store pressed keys in that list. You can find if a key is pressed by searching that list.
import keyboard
key_pressed = []
keyboard.press("a")
key_pressed.append("a")
keyboard.press("b")
key_pressed.append("b")
keyboard.press("c")
key_pressed.append("c")
#check if a specific key is pressed
if "a" in key_pressed:
print ("a pressed")
Print all pressed keys:
for key in key_pressed:
print(key,'pressed')
Print the last pressed key:
print(key_pressed[-1])
You can also create a class to make it easier to use:
import keyboard
class CustomKeyboard():
def __init__(self):
self.pressed_keys = []
def press(self, key):
keyboard.press(key)
self.pressed_keys.append(key)
def is_pressed_programmatically(self, key):
if key in self.pressed_keys:
return True
return False
Then use it like this:
kb = CustomKeyboard()
kb.press('a')
kb.press('b')
print("did a pressed programmatically?:")
print(kb.is_pressed_programmatically('a'))
print("did z pressed programmatically?:")
print(kb.is_pressed_programmatically('z'))
and here is the output:
is a pressed programmatically?: True
is z pressed programmatically?: False

Related

How to Stop mouse Listener with keyboard listener then back it again

I want m_listener to start only when I press f6 
and if I clicked F7, it stopped.
f6 it again to continue. 
My issue is that when I click f7 it totally stops the listener and on_x_y_click never run again if i clicked f6 
import pynput
import easygui
from pynput import *
def on_x_y_click(x, y, button, pressed):
print((x, y, button, pressed))
def on_release(key):
print(key)
if key == keyboard.Key.f6:
m_listener.join()
elif key == keyboard.Key.f7:
m_listener.stop()
elif key == keyboard.Key.esc:
# Stop listeners
m_listener.stop()
return False
# Collect events until released
with keyboard.Listener(on_release=on_release) as k_listener, \
mouse.Listener(on_click=on_x_y_click) as m_listener:
k_listener.join()
m_listener.join()
In the documentation, there doesn't seem to be any functionality to do this directly.
However, you can fairly easily implement this yourself by simply setting a flag and ignoring events when that flag is set:
from pynput import *
b_ignore_mouse_events = False
def on_x_y_click(x, y, button, pressed):
if b_ignore_mouse_events:
return
print((x, y, button, pressed))
def on_release(key):
global b_ignore_mouse_events
print(key)
if key == keyboard.Key.f6:
b_ignore_mouse_events = False
elif key == keyboard.Key.f7:
b_ignore_mouse_events = True
# Collect events until released
with keyboard.Listener(on_release=on_release) as k_listener, \
mouse.Listener(on_click=on_x_y_click) as m_listener:
k_listener.join()
m_listener.join()
Some notes about this:
Using global variables is often bad in larger applications, it seems fine for this simple script though.
Currently, the listeners are not stopped immediately when <ESC> is pressed.
Instead, you need to press a mouse button again because the mouse listener thread is waiting for the next key to be pressed and doesn't notice that it was stopped until that happens.
I just removed that part of the code because it did not function correctly. If you need that, you can ask another question about that (after searching for a solution yourself).

Stopping a python program when an arbitrary key was pressed while the program is running

I recently got to know about the python module signal. With that, we can capture a SIGINT and do what we want after capturing it. I used it as below. In that case I am just using SIGINT to print that program is going to be stopped and stop the program.
import signal
import os
import time
def signalHandler(signalnumb, frame):
print("Signal Number:", signalnumb, " Frame: ", frame)
print('Exiting the program...')
os._exit(0)
signal.signal(signal.SIGINT, signalHandler)
c=0
# Loop infinite times using the while(1) which is always true
while 1:
print(c)
#sleep for 1 seconds using the sleep() function in time
time.sleep(1)
c=c+1
Now I want to give any signal from keyboard(for example pressing 'q') and as soon as signal was recieved, the python program should be stopped. Has anyone got some experience on how to do that? Any other method rather than using signal module (for example using multithreading) is accepted.
Edit1-
Later I tried to use pynput module as suggested in one of a similar kind of question. For sure I have done a mistake. It doesn't work as I expected. It means with a key press, I couldn't stop the for loop from running.
from pynput import keyboard
import time
def on_press(key):
for i in range(100):
print(i)
time.sleep(1)
if key == keyboard.Key.esc:
return False # stop listener
try:
k = key.char # single-char keys
except:
k = key.name # other keys
if k in ['1', '2', 'left', 'right']: # keys of interest
# self.keys.append(k) # store it in global-like variable
print('Key pressed: ' + k)
return False # stop listener; remove this if want more keys
listener = keyboard.Listener(on_press=on_press)
listener.start() # start to listen on a separate thread
listener.join() # remove if main thread is polling self.keyspython
Can someone point out how to do it using pynput in correct way?
This was my original implementation:
a = input('Press a key to exit')
if a:
exit(0)
However, it seems that you need a piece of code that will allow for any key to be clicked and immediately exit out of the program, without hitting enter afterwards. This may be a better way to do that:
import readchar
print("Press Any Key To Exit")
k = readchar.readchar()
Hope this helps!
After carefully understanding about the threads and pynput module, I managed to stop a for loop (or any program which runs as a separate thread) using a key press callback.
from pynput import keyboard
import os
import threading
import time
loop_running = False
def on_press(key):
print(dir(key))
global loop_running
#if key == keyboard.Key.delete and not loop_running:
if ('char' in dir(key)) and (key.char == 's') and (not loop_running):
t=threading.Thread(target=start_loop)
t.start()
#elif key==keyboard.Key.tab: #if this is used, the attributes related to 'key' will be changed. you can see them since I have used a print(dir(key))
elif key.char == 'q':
loop_running=False
def start_loop():
global loop_running
loop_running = True
for i in range(100):
if not loop_running:
os._exit(0)
print(i)
time.sleep(1)
with keyboard.Listener(on_press=on_press) as listner:
listner.join()

how to terminate pynput keyboard listener with a timer or a pressed key?

I know how to terminate pynput keyboard listener with a timer or with a specific key to be pressed (they are both discussed here in this forum).
I cannot find a way to mix the 2 so that the listener is terminated after a preset amount of time or before that time if a specific key (Key.end for example is pressed)
I tried many combinations but none of them work so far.
added after #martineau comments:
an apparently natural way to do it could be:
from pynput import keyboard as kb
from time import time
exit_script=False
def action_press(key):
global exit_script
if key == kb.Key.end:
print ('end pressed')
exit_script= True
return False
timeOn=10
t0=time()
with kb.Listener(on_press=action_press,suppress=True) as l:
while exit_script== False and time()-t0<timeOn:
pass
l.join()
print('listener terminated')
But it fails to terminate with the timer criteria.
Does somebody know how correct this or if there is an alternative approach??
Thks
Ok I just found one answer using pynput controller. It does the trick.
from pynput import keyboard as kb
from time import time
mykb=kb.Controller()
exit_script=False
def action_press(key):
global exit_script
if key == kb.Key.end:
print ('end pressed')
exit_script= True
return False
timeOn=10
t0=time()
with kb.Listener(on_press=action_press,suppress=True) as l:
while exit_script== False:
if time()-t0>timeOn:
mykb.press(kb.Key.end)
mykb.release(kb.Key.end)
l.join()
print('listener termianted')

How to add On and Off buttons with pyautogui?

I made a simple auto-clicker in python. Every three seconds the script clicks wherever your mouse is. How would I add "on and off" keys? I imagine it is a simple if/else statement but I don't know how to write it.
As of Wed Sep 15 12:10, I do not have an answer that works well.
import pyautogui
import time
def Auto_Click():
width, height = pyautogui.position()
pyautogui.click(width, height)
time.sleep(3)
while True:
Auto_Click()
I'd suggest listening to specific key presses indefinitely to switch clicking on and off. And as there is an indefinite loop for the clicking as well, you will need multithreading (to perform clicking and listening for key presses simultaneously).
Notes
The auto clicker is switched off by default right on start (To avoid clicks at unwanted positions on screen right after running it). Press SHIFT to toggle it after pointing the mouse at wanted position.
Press ESC to exit the program.
I have used SHIFT and ESC keys for toggles so that the key presses won't show up in the next prompt unlike the character keys.
Use the below code if you really need to use pyautogui. Here is the solution using pynput for handling both mouse and keyboard. (My code is basically a modified version which uses keyboard module and pyautogui instead)
import time
import keyboard
import pyautogui
import threading
INTERVAL = 0.5 # Time interval between consecutive clicks
DELAY = 0.5 # Time delay between consecutive program cycles [after the clicks are turned off]
TOGGLE_KEY = 'shift' # Key to toggle the clicking
EXIT_KEY = 'esc' # Key to stop and exit from the program
class AutoClicker(threading.Thread):
def __init__(self, interval, delay):
super(AutoClicker, self).__init__()
self.interval = interval
self.delay = delay
self.running = False
self.program_running = True
def start_clicking(self):
self.running = True
def stop_clicking(self):
self.running = False
def exit(self):
self.stop_clicking()
self.program_running = False
def toggle_clicking(self):
if self.running:
self.stop_clicking()
else:
self.start_clicking()
def click(self):
width, height = pyautogui.position()
pyautogui.click(width, height)
# This function is invoked when the thread starts.
def run(self):
while self.program_running:
while self.running:
self.click()
time.sleep(self.interval)
time.sleep(self.delay)
if __name__ == '__main__':
# Run indefinite loop of clicking on seperate thread
auto_clicker_thread = AutoClicker(INTERVAL, DELAY)
auto_clicker_thread.start() # Invokes run() function of the thread
# So that we can listen for key presses on the main thread
keyboard.add_hotkey(TOGGLE_KEY, lambda: auto_clicker_thread.toggle_clicking())
keyboard.add_hotkey(EXIT_KEY, lambda: auto_clicker_thread.exit())

How to use threads to get input from keyboard in python 3?

I am making a snake game which requires the player to press the WASD keys without stopping the game process to to get input from player. So I can't use input() for this situation because then the game stops ticking to get input.
I found a getch() function which immediately gives input without pressing enter, but this function also stops game ticking to get input like input(). I decided to use threading module to get input via getch() in different thread. The problem is that getch() isn't working while in different thread and I'm not sure why.
import threading, time
from msvcrt import getch
key = "lol" #it never changes because getch() in thread1 is useless
def thread1():
while True:
key = getch() #this simply is almost ignored by interpreter, the only thing it
#gives is that delays print() unless you press any key
print("this is thread1()")
threading.Thread(target = thread1).start()
while True:
time.sleep(1)
print(key)
So why getch() is useless when it is in thread1()?
The problem was that you're creating a local variable key inside thread1 instead of overwriting the existing one. The quick-and-easy solution would be to declare key to be global inside thread1.
Finally, you should consider using locks. I don't know if it's necessary or not, but I'd imagine weird things could happen if you try and write a value to key in the thread while printing it out at the same time.
The working code:
import threading, time
from msvcrt import getch
key = "lol"
def thread1():
global key
lock = threading.Lock()
while True:
with lock:
key = getch()
threading.Thread(target = thread1).start()
while True:
time.sleep(1)
print(key)
I tried using getch but it didn't work for me... (win7 here).
You can try using tkinter module // but I still can't make it running with threads
# Respond to a key without the need to press enter
import tkinter as tk #on python 2.x use "import Tkinter as tk"
def keypress(event):
if event.keysym == 'Escape':
root.destroy()
x = event.char
if x == "w":
print ("W pressed")
elif x == "a":
print ("A pressed")
elif x == "s":
print ("S pressed")
elif x == "d":
print ("D pressed")
else:
print (x)
root = tk.Tk()
print ("Press a key (Escape key to exit):")
root.bind_all('<Key>', keypress)
# don't show the tk window
root.withdraw()
root.mainloop()
As Michael0x2a says you may try using library made for game-making - pygame or pyglet.
#EDIT #Michael0x2a:
Are you sure your code works?
Whatever I press it always prints the same key.
#EDIT2:
Thanks!

Categories

Resources