Python Forum
Pynput - One content on screen, another in the variable
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Pynput - One content on screen, another in the variable
#1
Hi guys, I am using Pynput library to listen for the keyboard events and store typed keys in a variable, which I will use further for a text expansion.

However, I noticed, when I type too fast, that a simple code like the one below sometimes will place inside the accumulated_keys variable a different value from what I see on screen (which happens on any windows app). Sometimes I type "btw". I see "btw" on the screen but the variable shows "bwt" or vice-versa.

Why does this difference happen? Is there any workaround to solve it?

I am new to Python and programming, and I searched a lot about this. I know that inside specific apps (MS Word and others) I can get the value of the first word behind the cursor after I type, which is 100% reliable.

However, I want something more general and easy to implement in any input text window of the system. Is there a way to accomplish this? Any tips? My key listener must be fast and reliable. Thanks in advance.

from pynput.keyboard import Listener, Key

# Initialize variables
shift_pressed = False
accumulated_keys = ""

def press_on(key):
    global shift_pressed  # Declare it global to modify it inside the function
    if key == Key.shift or key == Key.shift_r:
        shift_pressed = True
    print(f"Press on {key}")

def press_off(key):
    global shift_pressed, accumulated_keys  # Declare them global to modify them inside the function

    if key == Key.shift or key == Key.shift_r:
        shift_pressed = False

    if hasattr(key, 'char') and key.char:  # Check if the key has a printable character
        if shift_pressed:
            accumulated_keys += key.char.upper()
        else:
            accumulated_keys += key.char

    print(f"Press OFF {key}, Accumulated keys: {accumulated_keys}")

    if key == Key.esc:
        return False

with Listener(on_press=press_on, on_release=press_off) as listener:
    listener.join()
buran write Sep-18-2023, 03:40 AM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
This time I added the python tags for you
Reply
#2
The difference between the typed keys and the variable accumulated_keys happens because Pynput is not able to keep up with the typing speed. When you type too fast, Pynput may miss some keystrokes or register them in a different order.

There are a few things you can do to improve the reliability of your code:


Use a lower-level keyboard event library, such as PyHook. PyHook is able to capture keystrokes more accurately and reliably than Pynput.

Use a different approach for text expansion. For example, you could use a natural language processing library to identify the words that are being typed and then expand them accordingly.
Use a different method for getting the value of the first word behind the cursor. For example, you could use the Windows API to get the text of the current window and then parse the text to find the first word.

Here is an example of how to use PyHook to listen for keyboard events:

[inline]import pyHook

def on_key_press(event):
global accumulated_keys

# Get the key that was pressed
key = event.Key

# If the key is a printable character, add it to the accumulated keys variable
if key.Ascii:
accumulated_keys += chr(key.Ascii)

# Create a HookManager object
hm = pyHook.HookManager()

# Register the on_key_press function to be called when a key is pressed
hm.KeyDown = on_key_press

# Hook the keyboard
hm.HookKeyboard()

# Start the event loop
hm.start()

# Wait for the user to press the escape key
while True:
if hm.isKeyPressed(pyHook.HookConstants.KEY_ESCAPE):
break

# Unhook the keyboard
hm.UnhookKeyboard()

# Print the accumulated keys variable
print(accumulated_keys)[/inline]

This code will be more reliable than the code in your original question, but it is also more complex.

If you want a simpler solution, you could use a different approach for text expansion. For example, you could use a natural language processing library to identify the words that are being typed and then expand them accordingly.
Reply
#3
(Sep-18-2023, 07:12 AM)Bolt Wrote: The difference between the typed keys and the variable accumulated_keys happens because Pynput is not able to keep up with the typing speed. When you type too fast, Pynput may miss some keystrokes or register them in a different order.

There are a few things you can do to improve the reliability of your code:


Use a lower-level keyboard event library, such as PyHook. PyHook is able to capture keystrokes more accurately and reliably than Pynput.

Use a different approach for text expansion. For example, you could use a natural language processing library to identify the words that are being typed and then expand them accordingly.
Use a different method for getting the value of the first word behind the cursor. For example, you could use the Windows API to get the text of the current window and then parse the text to find the first word.

Here is an example of how to use PyHook to listen for keyboard events:

[inline]import pyHook

def on_key_press(event):
global accumulated_keys

# Get the key that was pressed
key = event.Key

# If the key is a printable character, add it to the accumulated keys variable
if key.Ascii:
accumulated_keys += chr(key.Ascii)

# Create a HookManager object
hm = pyHook.HookManager()

# Register the on_key_press function to be called when a key is pressed
hm.KeyDown = on_key_press

# Hook the keyboard
hm.HookKeyboard()

# Start the event loop
hm.start()

# Wait for the user to press the escape key
while True:
if hm.isKeyPressed(pyHook.HookConstants.KEY_ESCAPE):
break

# Unhook the keyboard
hm.UnhookKeyboard()

# Print the accumulated keys variable
print(accumulated_keys)[/inline]

This code will be more reliable than the code in your original question, but it is also more complex.

If you want a simpler solution, you could use a different approach for text expansion. For example, you could use a natural language processing library to identify the words that are being typed and then expand them accordingly.



Thanks so much, Bolt. I will check PyHook. I will also search for a natural language processing library. I need a more robust code.
Reply
#4
I think the reason is programs take keyboard input when you press the key, not when you release the key. See if this code does what you want.
from pynput.keyboard import Listener, Key
 
# Initialize variables
shift_pressed = False
accumulated_keys = ""
 
def press_on(key):
    global shift_pressed, accumulated_keys 

    if key == Key.shift or key == Key.shift_r:
        shift_pressed = True
    if hasattr(key, 'char') and key.char:  # Check if the key has a printable character
        if shift_pressed:
            accumulated_keys += key.char.upper()
        else:
            accumulated_keys += key.char

 
def press_off(key):
    global shift_pressed
 
    if key == Key.shift or key == Key.shift_r:
        shift_pressed = False
 
    print(f"Accumulated keys: {accumulated_keys}")
 
    if key == Key.esc:
        return False
 
with Listener(on_press=press_on, on_release=press_off) as listener:
    listener.join()
Valjean likes this post
Reply
#5
[quote="deanhystad" pid='172986' dateline='1695035717']
I think the reason is programs take keyboard input when you press the key, not when you release the key. See if this code does what you want.
[python]from pynput.keyboard import Listener, Key

Yes. It does. But I've already changed to the keyboard library. I will explain:

1. It creates its own thread, so I write less code.

2. When I press and hold backspace, all the backspaces sent and stored in my var match the number of backspaces currently on my screen. When using pynput, even holding backspace only one backspace press was sent. Maybe I needed to do more research, but keyboard is giving everything I need out of the box. Its very important to me: to keep the listener variable equal to what is on screen. So I guess the keyboard library is more low level than pyinput. Thanks a lot anyway. It always good to learn and try new things
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Problem with "from pynput" LFreitas 1 208 Mar-24-2024, 02:23 AM
Last Post: deanhystad
  Pynput Library not Working with Windows jacknewport 1 2,361 Mar-26-2022, 07:09 PM
Last Post: snippsat
  Solve Pynput Problem when changing language? ppel123 0 2,283 Feb-19-2020, 03:38 PM
Last Post: ppel123
  import pynput Sherlock42 2 3,356 Feb-08-2020, 04:01 PM
Last Post: Sherlock42
  Pynput doesn't recognize shift button and special characters VirtualDreamer 0 3,037 Jul-17-2019, 11:55 AM
Last Post: VirtualDreamer
  Pynput - no capital letters jmair 2 4,567 Feb-12-2019, 09:53 PM
Last Post: jmair
  wn = turtle.screen() AttributeError: module 'turtle' has no attribute 'screen' Shadower 1 6,191 Feb-06-2019, 01:25 AM
Last Post: woooee
  Problem with updating file to attach/pynput jameseroni 7 4,477 Nov-02-2018, 03:47 AM
Last Post: jameseroni
  Pynput while not using text field Superbit 2 4,259 Mar-07-2018, 04:35 AM
Last Post: Superbit

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020