I would like to call a python function or a python file to execute with a hotkey. My function is an infinite loop. I would like to terminate the execution as well with a hotkey. Is it possible?
Windows shortcuts is not an option, because I would like to choose 1 key to execute and not a combination of keys ( for example CRTL + SHIFT + L ) .
It would be nice if the hotkey would be the middle ( scroll ) button of the mouse.
You can setup a hotkey using the mouse module (pip install mouse). To run a function in a loop and then stop in on the hotkey again, you will need to create a new thread (intro to threading) that will run this loop. Then you will want to use an event to stop this thread's execution when the hotkey is pressed again. Below is an example of an implementation that does this.
import mouse # pip install mouse
import threading
import time
def your_function():
# the function you want to execute in the loop
print("Executing your function...")
time.sleep(0.9) # You will probably want to remove this
def repeat_function(kill_event):
print("Starting loop...")
while not kill_event.is_set():
your_function()
print("Exiting loop...")
while True:
print("Waiting for hotkey press...")
kill_event = threading.Event()
new_thread = threading.Thread(target=lambda: repeat_function(kill_event))
# set the hotkey that will start the loop to middle mouse button click
start_hook = mouse.on_middle_click(new_thread.start)
mouse.wait(mouse.MIDDLE, mouse.UP) # wait till the hotkey gets released
mouse.unhook(start_hook) # clear the old hotkey
# set the hotkey to kill the loop instead
end_hook = mouse.on_middle_click(kill_event.set)
mouse.wait(mouse.MIDDLE, mouse.UP)
new_thread.join() # wait for thread to fully finish, unnecessary
mouse.unhook(end_hook) # remove kill hotkey and start again
Related
I have a Python script that sends keystrokes, what I'm trying to do is, run the script in a process that's in the background. Meaning that while I'm focused on a different process for example chrome.exe, I want the script to send keystrokes to a process in the background for example test.exe.
Try this.
import keyboard
import time
while True:
time.sleep(.1)
k = keyboard.get_hotkey_name()
if k=='p': # change s with another key
print('p is pressed')
# OR #
if keyboard.is_pressed(hotkey='p'):
print('p is pressed')
Note: If you want to add a event on ctrl+s or ctrl+p You just need to change p to ctrl+p or anything you want.
I need to stop a program when a keyboard key q is pressed. How can I achieve that in the below code? How can I ignore time.sleep & detect a keypress & exit the program by printing something? Currently, the keypress gets detected only after 10 seconds. Suppose I am pressing q after 3 seconds the program doesn't exit.
import sys
import time
import keyboard
def hd():
print("Hi")
time.sleep(10)
if keyboard.is_pressed("q"):
print(keyboard.is_pressed("q"))
sys.exit()
while True:
hd()
Instead of polling the keyboard to check if a certain key is pressed, you can just add a hotkey. When the hotkey q (or whatever key you like) is pressed, a trigger function quit is called.
import keyboard
import time
import sys
exitProgram = False
# prepare to exit the program
def quit():
global exitProgram
exitProgram=True
# set hotkey
keyboard.add_hotkey('q', lambda: quit())
# main loop to do things
while not exitProgram:
print("Hello")
time.sleep(1)
print("bye bye")
sys.exit()
time.sleep() is a blocking call. Nothing happens in your program while it runs.
Shorten the intervals. For example, instead of sleeping 10 seconds, sleep 100 × 0.1 second.
import sys
import time
import keyboard
def hd():
print("Hi")
for _ in range(100):
time.sleep(0.1)
if keyboard.is_pressed("q"):
print(keyboard.is_pressed("q"))
sys.exit()
while True:
hd()
For more complex behavior (doing actual work while also listening for a keyboard event) you will have to look into multithreading.
If you create a hotkey activated by '1' and a hotkey activated by '2' before hotkey 1's function is finished then the second hotkey won't call it's assigned function.
I can create hotkeys which call functions in python using the keyboard module. I have included an example in the following code which works for calling functions.
import keyboard
import time
def hk_print(word,word2):
time.sleep(5)
print(word,word)
keyboard.add_hotkey('1', hk_print, args= ('hotkey','test1'))
keyboard.add_hotkey('2', hk_print, args= ('hotkey','test2'))
keyboard.wait()
Is there anything I can add to this code to make the hotkey activated functions interrupt eachother? So If I press 1 and then 2 it will stop going through the first function and start doing the second one right away?
It's doable, but it'll take some effort. You're getting into async territory.
What I would suggest is using keyboard.record() and using threading.Timer() in lieu of time.sleep(). You would create a new callback which would start recording all the events as soon as the key is pressed and start a Timer() with some timeout, let's say 5 seconds.
After the time has elapsed, you would have to stop recording, then evaluate the events that were captured during the wait, drop all the 1s and 2s pressed save for the very last one, then call ANOTHER callback - hk_print with the right set of args, depending on what the final press was.
I wrote a program that catches keyboard events on tetris, which was opened via a subprocess. no when i only want to catch events without opening Tetris my keyboard handler (pyHook) won't catch the events:
# open Tetris
#Tetris = subprocess.Popen (["C:\\Program Files (x86)\Tetris\Tetris.exe"])
#start game by sending "F2" key
#for hwnd in get_hwnds_for_pid (Tetris.pid):
#win32gui.PostMessage (hwnd, win32con.WM_KEYDOWN, win32con.VK_F2, 0)
keyH = KeyHandler()
ToneT = ToneTimer()
keyH.setDaemon(True)
ToneT.setDaemon(True)
keyH.start()
ToneT.start()
this worked fine when using another subprocess but i do not know why this is stuck now. KeyHandler won't pick up the keys.
class KeyHandler(threading.Thread):
def run(self):
# watch for all keyboard events
KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
# set the hook
KeyHandler.hm.HookKeyboard()
# activate message pipeline
print "keyboardhooked"
pythoncom.PumpMessages()
print "thisshouldnotbeprinted"
all but the last statement gets printed, but evoked keys are not printed, but instead when i press a key the app freezes (I have a function to catch events that works fine with the subprocess included...)
I guess there is something wrong with the threading when leaving out the subprocess.
I found out that if I switch to another GUI window before pressing a key, and then press a key, keys get accepted and keyevents printed.
More precise info: This probably has to do with running the script inside of OpenSesame (experimental software) as somehow pyhook does not retrieve keyboard responses from his window / windows in general but only from gui windows?! Thus I might need to seek an alternative for pyhook here ?
It's not working due to Qt bypassing the windows message loop -- pyHook relies on the message loop to function.
Modern graphical interface frameworks, such as Windows Forms, Windows
Presentation Foundation, MFC, Delphi, Qt and others do not
typically require applications to directly access the Windows message
loop, but instead automatically route events such as key presses and
mouse clicks to their appropriate handlers as defined within the
framework.
From Message Loop in MS Windows
The run method needs to have a loop. The thread ends once it leaves the run method.
class KeyHandler(threading.Thread):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.running = True
# end
def run(self):
while self.running:
# watch for all keyboard events
KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
# set the hook
KeyHandler.hm.HookKeyboard()
# activate message pipeline
print "keyboardhooked"
pythoncom.PumpMessages()
print "thisshouldnotbeprinted"
# end run
or You don't have to subclass it at all.
def catch_key(keyboard):
# watch for all keyboard events
KeyHandler.hm.KeyDown = keyboard.OnKeyboardCharEvent
# set the hook
KeyHandler.hm.HookKeyboard()
# activate message pipeline
print "keyboardhooked"
pythoncom.PumpMessages()
print "thisshouldnotbeprinted"
thread = threading.Thread(target=catch_key, args=(Keyboard()))
thread.start()
# Remember to use thread.join() to safely close the thread.
I am working on some code that will count mouse clicks on a beaglebone board in python.
My question is how to structure code such that I can access the total number of mouse clicks while the mouse click counter function is still running (indefinately), and at the same time not interrupt the mouse click counter function ( i dont want to miss a click!)?
Is there a way to access variables in a running function in python without interrupting it?
Depends on how you're wanting to access them. An infinite loop works well.
_clicks = 0
while True:
if _clicks != clicks:
_clicks = clicks
print(_clicks)
But you'll almost certainly have to put that in another thread.
from threading import Thread
def notifier():
global clicks # whatever variable you're accessing
while True:
if _clicks != clicks:
_clicks = clicks
print(_clicks)
t = Thread(target=notifier)
t.start()