0

I'm using PyAutoGUI to listen for a keyboard shortcut, but when I press the shortcut keys, it triggers Google Chrome's Ctrl + F and Ctrl + O shortcuts to find in page and open a file.

However, I do not want it to trigger Chrome's shortcuts. I only want it to trigger a function in my code.

I'm using this:

keyboard.wait("ctrl + f + o", suppress=True, trigger_on_release=True)

But, unfortunately, it does not trigger the function in my code, and it also triggers Chrome's shortcuts (should not).

I want the shortcut to trigger only the function in my code. It shouldn't trigger the foreground window's (for example, Chrome's) shortcuts. How can I do this?

The Amateur Coder
  • 789
  • 3
  • 11
  • 33

1 Answers1

1

Yes, this is possible - but more complicated. You can use low-level keyboard hooks (just an example).

def callback(n, w, l):

    if keyboard.is_pressed("ctrl") and keyboard.is_pressed("f") and keyboard.is_pressed("o"):
        perform_action()
        return -1 

    return 0

whereas perform_action() is the method you're calling once the shortcut is performed. Both return statements are crucial as they signal whether they shortcut was executed or not.

Now we have to implement the keyboard hook on a low-level:

from ctypes import windll, c_void_p, c_int, CFUNCTYPE

PROC = CFUNCTYPE(c_int, c_int, c_void_p, c_void_p)

CMPFUNC = PROC(callback)

hook = windll.user32.SetWindowsHookExA(
    c_int(13),
    CMPFUNC,
    c_int(0),
    c_int(0)
)

Explanation: FUNC specifies the callback function via a void pointer. Important is hook = windll.user32.SetWindowsHookExA - with the four arguments provided:

  1. c_int(13) is the type of hook we are intending to set: type 13 equals to WH_KEYBOARD_LL which is a low-level keyboard hook!
  2. FUNC is the callback function that will be called when the keypress is detected
  3. and 4. handle the DLL and thread ID which are both not necessary in this simple example and thus we pass a 0.

So you can now use keyboard.wait("ctrl + f + o", suppress = True, trigger_on_release = True) as long as you follow with the Unhook statement (for when the shortcut keys were actually pressed):

windll.user32.UnhookWindowsHookEx(hook_id).

The full code may be something along the lines of:

import keyboard
from ctypes import windll, c_void_p, c_int, CFUNCTYPE

def perform_action():
   #...

def callback(n, w, l):
   # As specified above

PROC = CFUNCTYPE(c_int, c_int, c_void_p, c_void_p)

CMPFUNC = PROC(callback)

hook = windll.user32.SetWindowsHookExA(
    c_int(13),
    CMPFUNC,
    c_int(0),
    c_int(0)
)

# Your original keyboard.wait - by the way, you might not need the two parameters?
keyboard.wait("ctrl + f + o", suppress=True, trigger_on_release=True) 

# Unhook:
windll.user32.UnhookWindowsHookEx(hook_id)
J. M. Arnold
  • 6,261
  • 3
  • 20
  • 38
  • Sorry for the late reply. I tried the code, but I'm getting this error: `FUNC = c_void_p(callback) TypeError: cannot be converted to pointer`. Do I have to use Cython or CPython to use this code? I changed `CMPFUNC` to `FUNC`. What could I be doing wrong? – The Amateur Coder Dec 14 '22 at 16:27
  • 1
    Included a [CFUNCTYP factory function](https://docs.python.org/3/library/ctypes.html) !:) – J. M. Arnold Dec 14 '22 at 16:37