Giter VIP home page Giter VIP logo

Comments (28)

moses-palmer avatar moses-palmer commented on July 20, 2024 13

Thank you!

The listener does not maintain a list of the currently pressed keys---this would be a new, quite reasonable, feature.

You can however emulate that using the following snippet:

from pynput import keyboard

# The key combination to check
COMBINATION = {keyboard.Key.cmd, keyboard.Key.ctrl}

# The currently active modifiers
current = set()


def on_press(key):
    if key in COMBINATION:
        current.add(key)
        if all(k in current for k in COMBINATION):
            print('All modifiers active!')
    if key == keyboard.Key.esc:
        listener.stop()


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()

This works as expected, with the following caveat: If one of the modifier keys is pressed before the listener is started, it has to be released and pressed again for the program to detect it.

I will update the road map in the wiki.

from pynput.

a2435191 avatar a2435191 commented on July 20, 2024 5
from pynput import keyboard
from pynput.keyboard import Key
from threading import Thread
import time

COMBINATION = {
    Key.down,
    Key.up,
    Key.left
    #Key.right
}



# The key combination to check


# The currently active modifiers
current = set()


def on_press(key):
    if key in COMBINATION:
        current.add(key)
        if all(k in current for k in COMBINATION):
            print('All modifiers active!')
    if key == keyboard.Key.esc:
        listener.stop()


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()

I was looking for a way to detect multiple keypresses at once, and this seemed to be the answer. The only problem is that, for whatever reason, only two keys can be returned at the same time (with occasional exception). How do I detect 3+ keyboard inputs at once?

from pynput.

gaoyaoxin avatar gaoyaoxin commented on July 20, 2024 1

@arcadeperfect

from pynput import keyboard

# The key combination to check
COMBINATION = {keyboard.Key.shift, keyboard.Key.ctrl, keyboard.Key.alt, keyboard.KeyCode.from_char('x')}

# The currently active modifiers
current = set()


def on_press(key):
    if key in COMBINATION:
        current.add(key)
        if all(k in current for k in COMBINATION):
            print('All modifiers active!')
    if key == keyboard.Key.esc:
        listener.stop()


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()

keyboard.KeyCode.from_char('x') or its alike works fine for alphanumeric keys.
Please note that the order of keys pressed is important. Beginning with alt and then x works fine while others may not.
In my point of view, this might have something to with system-built-in shortcuts which often combines ctrl, alt and shift. However, without shift, ctrl+alt+x works fine in any order.
Hope the designer (@moses-palmer) for this phenomenal package which can listen/monitor global keyboard events have some view on this!

from pynput.

GlassGruber avatar GlassGruber commented on July 20, 2024 1

Sorry to bump this up again, is just a question because maybe I'm misunderstanding something.
I've used your code above for this type of hotkey combination ctrl + c.
It wasn't working, so I fiddled a bit suspecting that maybe the key press wasn't fired properly, instead it was, but the key returned is not the one expected:

In [1]: from pynput import keyboard

In [2]: keyboard.Key.ctrl
Out[2]: <Key.ctrl: <17>>

In [3]: keyboard.Key.ctrl_l
Out[3]: <Key.ctrl_l: <162>>

keyboard.Key.ctrl is what I set in my script, but keyboard.Key.ctrl_l is instead what is caught by the listener; with this mismatch the condition to print the message is never met.
Am I wrong assuming that Key.ctrl should be considered a general alternative for Key.ctrl_l or Key.ctrl_r?
I'm on a Windows 7-64bit with Python 3.7

Thank you!

from pynput.

521xueweihan avatar 521xueweihan commented on July 20, 2024

Thank you for your replay, I learn a lot ๐Ÿ™

from pynput.

arcadeperfect avatar arcadeperfect commented on July 20, 2024

Hi,

Thanks for the lib

How can I combine your above example with a regular character to create a hotkey?

IE shift+ctrl+alt+x causes some code to execute.

Thanks

from pynput.

devxpy avatar devxpy commented on July 20, 2024

@moses-palmer How are you able to modify the current varialbe from a function. AFAIK, it's not possible to modify global variable from inside a function in python without explicity declaring global in your function definition.

https://stackoverflow.com/questions/4522786/modifying-a-global-variable-inside-a-function

from pynput.

moses-palmer avatar moses-palmer commented on July 20, 2024

from pynput.

devxpy avatar devxpy commented on July 20, 2024

That explains it, thanks!

Here is a modified version that also prints a message when you release that key combination!

class MockButton:
    def __init__(self, *keys):
        self.combination = {*keys}
        self.currently_pressed = set()
        self.is_pressed = False

        listener = Listener(on_press=self._on_press, on_release=self._on_release)
        listener.start()

    def _on_press(self, key):
        if key in self.combination:
            self.currently_pressed.add(key)

        if self.currently_pressed == self.combination:
            self.is_pressed = True
            print('pressed!')

    def _on_release(self, key):
        try:
            self.currently_pressed.remove(key)

            if self.is_pressed and len(self.currently_pressed) == 0:
                self.is_pressed = False
                print('released!')

        except KeyError:
            pass


if __name__ == '__main__':
    btn = MockButton(Key.alt, Key.ctrl)
    input()

from pynput.

moses-palmer avatar moses-palmer commented on July 20, 2024

You are correct in assuming that Key.ctrl is supposed to be a generic alternative to Key.ctrl_l and Key.ctrl_r.

In the case of comparisons, however, things do get a bit tricky, especially considering keycode_set_1 == keycode_set_2.

I guess a possible solution is to add a set of alternate key codes for each KeyCode instance, and override __eq__ and __hash__. In the mean time, you can use key in (Key.ctrl, Key.ctrl_l, Key.ctrl_r) when doing comparisons. That would not work for the code in @devxpy 's comment though.

from pynput.

GlassGruber avatar GlassGruber commented on July 20, 2024

In the mean time, you can use key in (Key.ctrl, Key.ctrl_l, Key.ctrl_r) when doing comparisons. That would not work for the code in @devxpy 's comment though.

Thank you @moses-palmer, I thought so, this is the code I used if anyone needs this. As a small bonus the following code listens only to double pressing of ctrl + c in less than a second:

from pynput import keyboard
import datetime

# The key combinations to check
COMBINATIONS = [
    {keyboard.Key.ctrl_l, keyboard.KeyCode(char='c')},
    {keyboard.Key.ctrl_r, keyboard.KeyCode(char='c')}
]

# The currently active modifiers
current = set()

tnow = datetime.datetime.now()
tcounter = 0

def on_press(key):
    if any([key in comb for comb in COMBINATIONS]):
        current.add(key)
        if any(all(k in current for k in comb) for comb in COMBINATIONS):
            global tnow
            global tcounter
            tcounter += 1
            if datetime.datetime.now() - tnow < datetime.timedelta(seconds=1):
                if tcounter > 1:
                    tcounter = 0
                    main_function()
            else:
                tnow = datetime.datetime.now()
    if key == keyboard.Key.esc:
        listener.stop()


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

def main_function():
    print('Main function fired!')
    # rest of your code here...

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

If you have comments on above code to improve it or any suggestions (in particular the rather sloppy global declare, I'm still learning) please let me know!

Thank you.

from pynput.

gabycperezdias avatar gabycperezdias commented on July 20, 2024

Hi @GlassGruber, I tried your code, but for some reason (I am on Windows) my "c" or "v" is like this only when pressed alone. When I have ctrl pressed, I get '\x16' '\x13' and don't match the combination.... @moses-palmer do you have any hint?

from pynput.

gabycperezdias avatar gabycperezdias commented on July 20, 2024

On a mac (changing the ctrl to cmd) I get the 'v' as 'โˆš'
@moses-palmer Is it possible that this is an issue? Or, is there a way to get the correct combination?

from pynput.

gabycperezdias avatar gabycperezdias commented on July 20, 2024

Does anyone have any idea how to fix this? @521xueweihan? @devxpy? @moses-palmer? @GlassGruber?

from pynput.

moses-palmer avatar moses-palmer commented on July 20, 2024

What version do you use? Commit 63aab1a, included in pynput 1.4, introduces a fix for keyboard layout handling, which is especially notable when running the application in a command window.

I have tested the code in @GlassGruber's comment using the master branch, which correctly identified Ctrl + C, and with that commit reverted, which caused your issue with only a non-ASCII key being generated.

Note that this on Windows; the Mac issue is different, and unresolved. Running the test script and printing all characters reveal that all alphanumeric keys are altered. Ctrl + C does work however; perhaps the simple and necessary solution is to add a special case for macOS? Using Cmd already mandates this.

from pynput.

gabycperezdias avatar gabycperezdias commented on July 20, 2024

@moses-palmer I've tried the pynput 1.4 and the master.

I've used this: (I also changed 'c' for 'v' and ctrl for cmd on mac)

    def on_press(key):
        print('KEY', key)
        try: print('CHAR', key.char) # letters, numbers et
        except: print('NAME', keyboard.KeyCode().from_char(key.name)) 

On Mac High Sierra - sequence: cmd / v / cmd+v
Both gave me:
KEY Key.cmd
NAME 'cmd'
KEY 'v'
CHAR v
vKEY Key.cmd
NAME 'cmd'
KEY 'โˆš'
CHAR โˆš
mainFunction was never fired. (If I changed the code to combine with ctrl instead of cmd, the main function is fired when 'v' is pressed 3 times after ctrl pressed (and not released) and 'v' is shown correctly)

Windows 10 and Ubuntu 18 - sequence: ctrl / v / ctrl+v
KEY Key.ctrl_l
NAME 'ctrl_l'
KEY 'v'
CHAR v
KEY Key.ctrl_l
NAME 'ctrl_l'
KEY 'v'
CHAR v
Main function is only fired if ctrl key is pressed and 'v' is typed twice. but the char is shown correctly

from pynput.

beterhans avatar beterhans commented on July 20, 2024

Hi GlassGruber Thanks

I edited your code so I can use in my script

basiclly I need to break a while loop. with ctrl - q
but the code demo is in a loop I can't run two loop in my script.
Luckliy I figure out to use start instead of join.
and ctrl shift keys are different on windows and mac
So I changed a bit to match all system.

Here is it

import pynput,time

is_quit = False

KeyComb_Quit = [
    {pynput.keyboard.Key.ctrl, pynput.keyboard.KeyCode(char='q')},
    {pynput.keyboard.Key.ctrl_l, pynput.keyboard.KeyCode(char='q')},
    {pynput.keyboard.Key.ctrl_r, pynput.keyboard.KeyCode(char='q')}

]

def on_press(key):
    global is_quit
    if any([key in comb for comb in KeyComb_Quit]):
        current.add(key)
        if any(all(k in current for k in comb) for comb in KeyComb_Quit):
            is_quit = True

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


# The currently active modifiers
current = set()

listener = pynput.keyboard.Listener(on_press=on_press, on_release=on_release)
listener.start()

##### MAIN Script #####
while True:
    do something
    time.sleep(0.00833)
    if is_quit:
        break

from pynput.

SpecialCharacter avatar SpecialCharacter commented on July 20, 2024

Sorry, I don't get it how to use join(key) correctly.
I have the listener output "ยดs". How do I turn it into "ล›"?

from pynput.

redstoneleo avatar redstoneleo commented on July 20, 2024

Any progress on implement the key combination feature for this project ?

from pynput.

GarrettStrahan avatar GarrettStrahan commented on July 20, 2024

So I wrote some code & its causing me a headache. I want to have two different combinations. The code I wrote the first combination of keys Ctrl+G or g work but Ctrl + H or h did not but crashes the program. I have re-written my code so many times trying to figure it out that it does not makes sense. Any help? Please help me! I'm really new to python!

Indentation is not working, but if you look at the .txt it is there.

2_types_of_hotkeys.txt

from pynput import keyboard

COMBINATIONS = [
(keyboard.Key.ctrl_l, keyboard.KeyCode(char='g')),
(keyboard.Key.ctrl_l, keyboard.KeyCode(char='G'))]

COMBINATIONS2 = [
(keyboard.Key.ctrl, keyboard.KeyCode(char='h')),
(keyboard.Key.ctrl, keyboard.KeyCode(char='H'))]
#Not working just yet
current = set()

def execute1(): #Function call for character Ctrl G or Ctrl g
print("Detected hotkey")

def execute2(): #Function call for character Ctrl H or Ctrl h
print("Detected 2nd hotkey")

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):
execute1()
if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS2):
execute2()

def on_release(key):
if any([key in COMBO for COMBO in COMBINATIONS]):
current.remove(key)
if any([key in COMBO for COMBO in COMBINATIONS2]):
current.remove(key)

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

from pynput.

platelminto avatar platelminto commented on July 20, 2024

Using the given snippet, dead keys pollute the list and make further comparisons break. For example, when typing in Shift+t, a 'Shift' will be added, then a 'T'. If you then release the Shift, you are left with a 'T', and releasing 't' does not remove it, and the set now will keep containing 'T'.

Is there a way to disable registering dead keys?

As a current solution, I remove the last element in the list when a key is released, ignoring which key it actually is.

from pynput.

segalion avatar segalion commented on July 20, 2024

See
#182 (comment)

from pynput.

moses-palmer avatar moses-palmer commented on July 20, 2024

@platelminto, if you run into issues with dead keys, you may want to take a look at #118. The branch fixup-win32-keyboard-listener has a proposed solution.

from pynput.

pagujeeva avatar pagujeeva commented on July 20, 2024

How do we capture the windows super key events like (windows+r or windows+m etc)

from pynput.

SpecialCharacter avatar SpecialCharacter commented on July 20, 2024

Can you give an example?

from pynput.

yemreak avatar yemreak commented on July 20, 2024

Sorry about my last sentence, It's happened due to misunderstanding so I deleted it

from pynput.

SpecialCharacter avatar SpecialCharacter commented on July 20, 2024

@platelminto Had the same problem. I solved it by tracking the shift key down / shift key up movements.
It works also for windows super key events.

from pynput.

luizoti avatar luizoti commented on July 20, 2024
from pynput import keyboard
from pynput.keyboard import Key
from threading import Thread
import time

COMBINATION = {
    Key.down,
    Key.up,
    Key.left
    #Key.right
}



# The key combination to check


# The currently active modifiers
current = set()


def on_press(key):
    if key in COMBINATION:
        current.add(key)
        if all(k in current for k in COMBINATION):
            print('All modifiers active!')
    if key == keyboard.Key.esc:
        listener.stop()


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()

I was looking for a way to detect multiple keypresses at once, and this seemed to be the answer. The only problem is that, for whatever reason, only two keys can be returned at the same time (with occasional exception). How do I detect 3+ keyboard inputs at once?

It seems that pyinput can't handle three or more events at the same time, and where it always replaces the second key with the third. From the looks of it it does this without calling the on_release event.

from pynput.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.