Should I use <keypress> or <keyrelease> (or <Return>) for RFID scanning?

Viewed 245

I have a Raspberry Pi and an RFID scanner running a python script. I'm using tkinter to capture input using the following code.

from Tkinter import *
import Tkinter as tk

def __init__(self):
    command = tk.Tk()
    self.e = Entry(command)
    self.e.grid()
    self.e.focus_set()
    command.bind('<KeyPress>', self.key_input)
    command.mainloop()

def key_input(self, event):
    key_press = event.keysym
    if key_press == 'Return':
        time.sleep(0.5)
        self.enter()
    else:
        pass

def enter(self):
    //various API calls etc.  Here is where the RFID tag is often duplicated)

I'm getting some weird behaviour where the RFID tag is captured twice before a return is fired and I'm wondering if it is because of the order of operations.

Would binding using < keypress> vs < keyrelease> change anything? Or not because it's an RFID scanning and not a user pressing keys? Would using the < Return> be preferential? Or is the above code accomplishing the same thing?

1 Answers

What happens is that when you press and hold the key, OS generates multiple Press and Release events in a loop, not a single Press and single Release when the key is actually finally released. So, using only one of Press or Release events won't change anything.

One possibility is it to process both Press and Release events, and track the state of the key (if key is "being pressed") in event handlers. Now, this solves nothing as is, but then the trick is to also use after_idle to defer the processing of the Release event. after_idle schedules the execution on the next event loop, and after other events are processed, so:

def __init__(self):
    ...
    self.being_pressed = False
    command.bind('<KeyPress-Return>', key_input)
    command.bind('<KeyRelease-Return>', key_release)

def key_input(self, event):
    if not self.being_pressed:
        self.enter()

def key_release(self, event):
    self.being_pressed = True
    self.after_idle(self.do_release, event)

def do_release(self.event):
    self.being_pressed = False

This way, you still get all Press and Release events, but because Release events are now processed in next event loop, if key is being pressed for a long time, N+1 Press event handler will be executed before the N-th Release event handler, and thus will detect that the key is still being pressed.

Optionally, you can also use after cancel in Press event handler to cancel Release event processing at all.

Related