如何让 TkInter GUI(不是 shell 提示符)监听来自 /dev/stdin 的输入

Posted

技术标签:

【中文标题】如何让 TkInter GUI(不是 shell 提示符)监听来自 /dev/stdin 的输入【英文标题】:How to get a TkInter GUI (not a shell prompt) to Listen for Input from /dev/stdin 【发布时间】:2017-11-26 23:59:13 【问题描述】:

我正在编写一个带有 GUI 的 Python 程序,它侦听正在呈现的 RFID 令牌(使用模拟键盘输入的 USB 阅读器)。我遇到的问题是,只有在我启动脚本的终端具有焦点时,才会监听键盘/RFID 输入并采取行动。

如果 GUI 被聚焦,所有输入都会被忽略,但是当终端有焦点时,它可以正常工作,甚至会向 GUI 和 shell 发送更新。我试过在 GUI 上画一个文本输入框,但是没用。

我想知道这是否与它需要如何使用多个线程、队列甚至进程有关 - 谁能帮助我更好地理解它?

代码如下,提前致谢!

#!/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】:

在头疼之后,我再次找到this thread 并正确地重新阅读它,然后意识到它包含了我想要的答案,尽管我需要做一些工作。

我的listen_rfid 函数现在看起来像下面的代码。希望它对遇到我的问题的人有用!

关键是查看/dev/input/by-id 并找到您的设备,会有一个符号链接(至少在我的 Raspbian 安装中)指向更优雅的东西,例如,在我的情况下,“/event0” - 如以下)。然后就可以使用 python-evdev 来读取了。

如果你没有python-evdev,只需使用sudo pip install evdev安装即可

这是工作代码:

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 ]

【讨论】:

以上是关于如何让 TkInter GUI(不是 shell 提示符)监听来自 /dev/stdin 的输入的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Matplotlib 图形在 Tkinter GUI 中正确滚动+调整大小

如何将我的 python 逻辑应用到 tkinter GUI?这是一个简单的 GET 请求程序

pythonGUI编程-tkinter

如何将 Seaborn 绘图集成到 Tkinter GUI

如何让tkinter自动关闭窗口后显示另一个窗口?

tkinter:是不是可以将来自不同 tkinter 程序的 GUI 连接在一起并轻松地在它们之间导航?