3

I'm writing a Python programme with a GUI which listens for an RFID token being presented (using a USB reader which emulates keyboard input). I'm having issues whereby keyboard/RFID input is only listened for and acted upon if the Terminal from which I launch the script has focus.

If the GUI is focussed, all input is ignored but when the terminal has focus, it works fine and even sends updates to the GUI as well as shell. I've tried drawing a text input box on the GUI, but that didn't work.

I'm wondering if it's something to do with how it needs to use either multiple threads, queues or even processes - can anyone help me to understand it better?

Code is below, thanks in advance!

#!/usr/bin/env python3
import sys
import MySQLdb

try:
    # python 2
    import Tkinter as tk
    import ttk
except ImportError:
    # python 3
    import tkinter as tk
    from tkinter import ttk

from threading import Thread

class Fullscreen_Window:

def __init__(self):
    self.tk = tk.Tk()
    self.tk.title("Listening for RFID token...")
    self.frame = tk.Frame(self.tk)
    self.frame.pack()

    self.tk.attributes('-zoomed', True)
    self.state = False
    self.tk.bind("<F11>", self.toggle_fullscreen)
    self.tk.bind("<Escape>", self.end_fullscreen)
    self.tk.config(cursor="none")

    t = Thread(target=self.listen_rfid)
    t.daemon = True
    t.start()

def toggle_fullscreen(self, event=None):
    self.state = not self.state  # Just toggling the boolean
    self.tk.attributes("-fullscreen", self.state)
    return "break"

def end_fullscreen(self, event=None):
    self.state = False
    self.tk.attributes("-fullscreen", False)
    return "break"

def listen_rfid(self):
    dbHost = 'localhost'
    dbName = 'python'
    dbUser = 'python'
    dbPass = 'PASSWORD'

    dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName)
    cur = dbConnection.cursor(MySQLdb.cursors.DictCursor)

    with open('/dev/stdin', 'r') as tty:
        while True:
            RFID_input = tty.readline().rstrip()
            cur.execute("SELECT * FROM access_list WHERE rfid_code = '%s'" % (RFID_input))

            if cur.rowcount != 1:
                print("ACCESS DENIED")
                ttk.Label(self.tk, text="ACCESS DENIED").pack()
            else:
                user_info = cur.fetchone()
                print("Welcome %s!!" % (user_info['name']))
                ttk.Label(self.tk, text="Welcome %s!!" % (user_info['name'])).pack()
    tty.close()


if __name__ == '__main__':
w = Fullscreen_Window()
w.tk.mainloop()

1 Answers1

0

After much head scratching, I found this thread again and re-read it properly, then realised it contained the answer I was after, albeit with a tiny bit of work needed from me.

My listen_rfid function now looks like the code below. Hope it's useful for anyone stumbling across my problem!

The key thing is to look in /dev/input/by-id and find your device, there will be a symlink (at least there was on my Raspbian install) to something more elegant like, in my case, "/event0" - as below). You can then use python-evdev to read it.

If you don't have python-evdev, just install it with sudo pip install evdev

Here's the working code:

def listen_rfid(self):
    dbHost = 'localhost'
    dbName = 'python'
    dbUser = 'python'
    dbPass = 'PASSWORD'

    dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName) # ToDo: This needs some error handling for if MySQL has gone away, and reconnect.
    cur = dbConnection.cursor(MySQLdb.cursors.DictCursor)

    from evdev import InputDevice
    from select import select

    keys = "X^1234567890XXXXqwertzuiopXXXXasdfghjklXXXXXyxcvbnmXXXXXXXXXXXXXXXXXXXXXXX"
    dev = InputDevice('/dev/input/event0')
    rfid_presented = ""

    while True:
       r,w,x = select([dev], [], [])
       for event in dev.read():
            if event.type==1 and event.value==1:
                    if event.code==28:
                        #print("RFID: " + str(rfid_presented))

                        cur.execute("SELECT * FROM access_list WHERE rfid_code = '%s'" % (rfid_presented))

                        if cur.rowcount != 1:
                            #print("ACCESS DENIED")
                            ttk.Label(self.tk, text="ACCESS DENIED").pack()
                        else:
                            user_info = cur.fetchone()
                            #print("Welcome %s!!" % (user_info['name']))
                            ttk.Label(self.tk, text="Welcome %s!!" % (user_info['name'])).pack()

                        rfid_presented = ""
                    else:
                        rfid_presented += keys[ event.code ]