Search code examples
pythonkeyboard

Ctrl+Shift key combination in Python


I am working on a snippet that requires user interaction: either an Esc press or a Ctrl+Shift press.

I am struggling with combining Ctrl+Shift though. The below code works fine with the Esc button. However, when pressing Ctrl+Shift initially (before pressing Esc) it doesn't print anything, while after having pressed Esc at least once, it works for Shift but not for Ctrl- which is not what I intended. How can I bind Ctrl and Shift together?

from pynput import keyboard

COMBINATIONS = [
    { keyboard.Key.shift, keyboard.Key.ctrl },
    { keyboard.Key.esc }
]

# The currently active modifiers
current = set()

def execute():
    print ("Pressed!")

def on_press(key):
    if any([key in COMBO for COMBO in COMBINATIONS]):
        current.add(key)
        if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS):
            execute()

def on_release(key):
    try:
        current.remove(key)
    except KeyError:
        pass

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

Solution

  • If you put a print(key) at the start of on_press, you'll see that the CTRL key generates either a ctrl_l or ctrl_r key (depending on which one you've pressed), not a ctrl key.

    So you either have to handle either of them, ending up with some monstrous any/all/any code snippet, or just translate at the top:

    if key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:
        key = keyboard.Key.ctrl
    

    A version which handles this with a translation function xlate() (for CTRL and SHIFT keys, since the latter appear to generate shift/shift_r), as well as detecting releases of keys, can be seen below:

    from pynput import keyboard
    
    COMBINATIONS = [
        { keyboard.Key.shift, keyboard.Key.ctrl },
        { keyboard.Key.esc }
    ]
    
    current = set()
    
    def execute():
        print ("Pressed!")
    
    def xlate(key):
        if key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:
            return keyboard.Key.ctrl
        if key == keyboard.Key.shift_l or key == keyboard.Key.shift_r:
            return keyboard.Key.shift
        return key
    
    def on_release(key):
        key = xlate(key)
        if key in current:
            current.remove(key)
    
    def on_press(key):
        #print(key) # for debugging purposes.
        key = xlate(key)
        if any([key in COMBO for COMBO in COMBINATIONS]):
            current.add(key)
            if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS):
                execute()
    
    with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
        listener.join()