对关键事件使用 pynput 而不是 tkinter
using pynput for key events instead of tkinter
这是我的整个程序。我想从将 tkinter 与 gui 一起使用更改为对代码的 get_key 事件部分使用 pynput no gui。谁能告诉我要使用什么代码?此代码是一台会说话的自动售货机,它从机器内容列表中读取,这是一个由自动售货机公司更新的文件。
我不想使用图形用户界面,因为没有显示器。是覆盆子。
from gtts import gTTS
import pygame
from io import BytesIO
import sys
import time
import csv
pygame.init()
if sys.version_info[0] == 3:
# for Python3
from tkinter import *
else:
# for Python2
from Tkinter import *
def say(text):
tts = gTTS(text=text, slow=False, lang='en-us', lang_check=False)
fp = BytesIO()
tts.write_to_fp(fp)
fp.seek(0)
pygame.mixer.init()
pygame.mixer.music.load(fp)
pygame.mixer.music.play()
def load_list():
with open(r"/home/pi/VendyLogProject/vendylist.csv", mode="r") as infile:
return sorted(list(csv.reader(infile)))
def refresh_list():
global vl, vl2, baseposition
new_items = load_list()
if vl != new_items:
vl = new_items
vl2 = [item[0] for item in vl]
baseposition = vl[0]
vl = load_list()
vl2 = [item[0] for item in vl]
baseposition = vl[-1] # so when reading through it reads entry 0 first, then 1
def current(event=None):
global baseposition # baseposition was defined outside of the function, therefore we call global
say(baseposition[1]+baseposition[0])
def back(event=None):
global baseposition
currentposition = vl.index(baseposition)
if currentposition == 0:
baseposition = vl[-1]
say(baseposition[1]+baseposition[0])
else:
previousposition = int(currentposition) - 1 # previousposition is previous position
baseposition = vl[previousposition]
say(baseposition[1]+baseposition[0])
def forward(event=None):
global baseposition
currentposition = vl.index(baseposition)
if currentposition == (len(vl) - 1):
baseposition = vl[0]
say(baseposition[1]+baseposition[0])
else:
nextposition = int(currentposition) + 1 # nextposition is next position
baseposition = vl[nextposition]
say(baseposition[1]+baseposition[0])
def readnumber(int):
global vl
for item in vl:
global baseposition
currentposition = vl.index(baseposition)
if int == item[0]:
baseposition = vl[vl.index(item)]
say(baseposition[1]+baseposition[0])
def help():
say("Welcome to Vendy log! Use the plus and minus keys to go through the list of snacks or push a number to hear its contents!")
root = Tk()
prompt = ' VendyLog '
label1 = Label(root, text=prompt, width=len(prompt))
label1.pack()
#keys buffer
keybuf = []
def test_after():
if keybuf:
num = ''.join(keybuf)
keybuf.clear()
def get_key(event):
keybuf.append(event.char)
event.char = ''.join(keybuf)
root.after(500,test_after)
if event.char == '-':
back()
elif event.char == '+':
forward()
elif event.char == '.':
current()
elif event.char in vl2:
readnumber(event.char)
elif event.char == '00':
help()
elif event.char == '462.':
sys.exit()
def refresh_list_and_enqueue_next_refresh():
refresh_list()
root.after(60000, refresh_list_and_enqueue_next_refresh)
refresh_list_and_enqueue_next_refresh()
root.bind_all('<Key>', get_key)
root.mainloop()
我编辑了这条评论,因为在我玩过 OpenGL 和 Pygame 之后,我找到了如何使用 pynput 与 tkinter.
这是我编写的代码示例,用于测试它是否有效。
# // Imports
import tkinter, pynput
from tkinter import messagebox
# // Global variables
# // If we define variable type I found that will speed up execution a little
root:object = tkinter.Tk()
app_title:str = "Tkinter and Pynput"
app_size:tuple = (300, 150)
listener_stop:bool = False
# // Logics Keyboard
class Keyboard:
# On button pressed
# On my case I found the "Fn" button from laptop is not triggered...
@staticmethod
def Pressed(key) -> bool:
# If listener_stop is True then stop listening
if listener_stop: print("Keyboard Events are stoped!"); return False
# Else show pressed key
else: print(f"Keyboard pressed: {key}")
# On button released
@staticmethod
def Released(key) -> None:
print(f"Keyboard released: {key}")
# Listen keybboard buttons
@staticmethod
def Listener() -> None:
k_listen = pynput.keyboard.Listener(on_press=Keyboard.Pressed,
on_release=Keyboard.Released
)
k_listen.start()
# // Logics Mouse
class Mouse:
# On move
@staticmethod
def Move(x, y) -> bool:
# If listener_stop is True then stop listening
if listener_stop: print("Mouse Events are stoped!"); return False
else: print(f"Mouse: Moved to {x}x{y}")
# On scroll
@staticmethod
def Scroll(x, y, dx, dy) -> None:
where = "down" if dy < 0 else "up"
print(f"Mouse: Scrolled {where} at {x}x{y}")
# On click
# On my case I found mouse wheel press is not triggered...
@staticmethod
def Click(x, y, button, pressed) -> None:
action = "pressed" if pressed else "released"
print(f"Mouse: {button} was {action} at {x}x{y}")
# Listen keybboard buttons
@staticmethod
def Listener() -> None:
m_listen = pynput.mouse.Listener(on_move=Mouse.Move,
on_click=Mouse.Click,
on_scroll=Mouse.Scroll
)
m_listen.start()
# // Logics Define GUI
class MainApp:
def __init__(self, master):
self.master = master
# Create tkinter interface
self.X = (self.master.winfo_screenwidth() - app_size[0]) // 2
self.Y = (self.master.winfo_screenheight() - app_size[1]) // 2
self.master.wm_title(app_title)
self.master.wm_geometry(f"{app_size[0]}x{app_size[1]}+{self.X}+{self.Y}")
# Magic hapen here :P
self.Screen(self.master)
self.InputEvents()
# Define Screen Informations
def Screen(self, root) -> None:
# Set the main frame
self.frm_main = tkinter.Frame(root)
self.frm_main.pack(expand=True, fill="x", side="top")
# Defain frame components
self.title = tkinter.Label(self.frm_main, text=app_title, font=("Comic Sans MS", 18, "bold"), fg="tomato")
self.title.pack(expand=False, fill="x", side="top")
# Input events
def InputEvents(self) -> None:
Keyboard.Listener()
Mouse.Listener()
# Safe Quit
def SafeQuit(self, master:object = root) -> None:
global listener_stop
if messagebox.askokcancel(f"{app_title} Quit", f"Are you shore you want to quit {app_title}?"):
# We need to make shore if the window was closed and
# listening event are still runing, then make them stop
# You will see in fact they are not stoped (from console point view)
# for that reason we check listener_stop on Mouse Move and on Keyboard Key press.
# If for some reason the app is quit and listener has not stoped then on first action did will stop
# Mouse move after app quit >> Mouse listener will stop
# Keyboard button pressed after app quit >> Keyboard listener will stops
if listener_stop == False:
listener_stop = True
print("Events Listening are stoped!")
master.destroy()
# // Run if this is tha main file
if __name__ == "__main__":
app:object = MainApp(root)
root.protocol("WM_DELETE_WINDOW", app.SafeQuit)
root.mainloop()
我再次更新了代码,停止监听键盘和鼠标事件。
PS:它们的更新版本可以是found on my github
这是我的整个程序。我想从将 tkinter 与 gui 一起使用更改为对代码的 get_key 事件部分使用 pynput no gui。谁能告诉我要使用什么代码?此代码是一台会说话的自动售货机,它从机器内容列表中读取,这是一个由自动售货机公司更新的文件。
我不想使用图形用户界面,因为没有显示器。是覆盆子。
from gtts import gTTS
import pygame
from io import BytesIO
import sys
import time
import csv
pygame.init()
if sys.version_info[0] == 3:
# for Python3
from tkinter import *
else:
# for Python2
from Tkinter import *
def say(text):
tts = gTTS(text=text, slow=False, lang='en-us', lang_check=False)
fp = BytesIO()
tts.write_to_fp(fp)
fp.seek(0)
pygame.mixer.init()
pygame.mixer.music.load(fp)
pygame.mixer.music.play()
def load_list():
with open(r"/home/pi/VendyLogProject/vendylist.csv", mode="r") as infile:
return sorted(list(csv.reader(infile)))
def refresh_list():
global vl, vl2, baseposition
new_items = load_list()
if vl != new_items:
vl = new_items
vl2 = [item[0] for item in vl]
baseposition = vl[0]
vl = load_list()
vl2 = [item[0] for item in vl]
baseposition = vl[-1] # so when reading through it reads entry 0 first, then 1
def current(event=None):
global baseposition # baseposition was defined outside of the function, therefore we call global
say(baseposition[1]+baseposition[0])
def back(event=None):
global baseposition
currentposition = vl.index(baseposition)
if currentposition == 0:
baseposition = vl[-1]
say(baseposition[1]+baseposition[0])
else:
previousposition = int(currentposition) - 1 # previousposition is previous position
baseposition = vl[previousposition]
say(baseposition[1]+baseposition[0])
def forward(event=None):
global baseposition
currentposition = vl.index(baseposition)
if currentposition == (len(vl) - 1):
baseposition = vl[0]
say(baseposition[1]+baseposition[0])
else:
nextposition = int(currentposition) + 1 # nextposition is next position
baseposition = vl[nextposition]
say(baseposition[1]+baseposition[0])
def readnumber(int):
global vl
for item in vl:
global baseposition
currentposition = vl.index(baseposition)
if int == item[0]:
baseposition = vl[vl.index(item)]
say(baseposition[1]+baseposition[0])
def help():
say("Welcome to Vendy log! Use the plus and minus keys to go through the list of snacks or push a number to hear its contents!")
root = Tk()
prompt = ' VendyLog '
label1 = Label(root, text=prompt, width=len(prompt))
label1.pack()
#keys buffer
keybuf = []
def test_after():
if keybuf:
num = ''.join(keybuf)
keybuf.clear()
def get_key(event):
keybuf.append(event.char)
event.char = ''.join(keybuf)
root.after(500,test_after)
if event.char == '-':
back()
elif event.char == '+':
forward()
elif event.char == '.':
current()
elif event.char in vl2:
readnumber(event.char)
elif event.char == '00':
help()
elif event.char == '462.':
sys.exit()
def refresh_list_and_enqueue_next_refresh():
refresh_list()
root.after(60000, refresh_list_and_enqueue_next_refresh)
refresh_list_and_enqueue_next_refresh()
root.bind_all('<Key>', get_key)
root.mainloop()
我编辑了这条评论,因为在我玩过 OpenGL 和 Pygame 之后,我找到了如何使用 pynput 与 tkinter.
这是我编写的代码示例,用于测试它是否有效。
# // Imports
import tkinter, pynput
from tkinter import messagebox
# // Global variables
# // If we define variable type I found that will speed up execution a little
root:object = tkinter.Tk()
app_title:str = "Tkinter and Pynput"
app_size:tuple = (300, 150)
listener_stop:bool = False
# // Logics Keyboard
class Keyboard:
# On button pressed
# On my case I found the "Fn" button from laptop is not triggered...
@staticmethod
def Pressed(key) -> bool:
# If listener_stop is True then stop listening
if listener_stop: print("Keyboard Events are stoped!"); return False
# Else show pressed key
else: print(f"Keyboard pressed: {key}")
# On button released
@staticmethod
def Released(key) -> None:
print(f"Keyboard released: {key}")
# Listen keybboard buttons
@staticmethod
def Listener() -> None:
k_listen = pynput.keyboard.Listener(on_press=Keyboard.Pressed,
on_release=Keyboard.Released
)
k_listen.start()
# // Logics Mouse
class Mouse:
# On move
@staticmethod
def Move(x, y) -> bool:
# If listener_stop is True then stop listening
if listener_stop: print("Mouse Events are stoped!"); return False
else: print(f"Mouse: Moved to {x}x{y}")
# On scroll
@staticmethod
def Scroll(x, y, dx, dy) -> None:
where = "down" if dy < 0 else "up"
print(f"Mouse: Scrolled {where} at {x}x{y}")
# On click
# On my case I found mouse wheel press is not triggered...
@staticmethod
def Click(x, y, button, pressed) -> None:
action = "pressed" if pressed else "released"
print(f"Mouse: {button} was {action} at {x}x{y}")
# Listen keybboard buttons
@staticmethod
def Listener() -> None:
m_listen = pynput.mouse.Listener(on_move=Mouse.Move,
on_click=Mouse.Click,
on_scroll=Mouse.Scroll
)
m_listen.start()
# // Logics Define GUI
class MainApp:
def __init__(self, master):
self.master = master
# Create tkinter interface
self.X = (self.master.winfo_screenwidth() - app_size[0]) // 2
self.Y = (self.master.winfo_screenheight() - app_size[1]) // 2
self.master.wm_title(app_title)
self.master.wm_geometry(f"{app_size[0]}x{app_size[1]}+{self.X}+{self.Y}")
# Magic hapen here :P
self.Screen(self.master)
self.InputEvents()
# Define Screen Informations
def Screen(self, root) -> None:
# Set the main frame
self.frm_main = tkinter.Frame(root)
self.frm_main.pack(expand=True, fill="x", side="top")
# Defain frame components
self.title = tkinter.Label(self.frm_main, text=app_title, font=("Comic Sans MS", 18, "bold"), fg="tomato")
self.title.pack(expand=False, fill="x", side="top")
# Input events
def InputEvents(self) -> None:
Keyboard.Listener()
Mouse.Listener()
# Safe Quit
def SafeQuit(self, master:object = root) -> None:
global listener_stop
if messagebox.askokcancel(f"{app_title} Quit", f"Are you shore you want to quit {app_title}?"):
# We need to make shore if the window was closed and
# listening event are still runing, then make them stop
# You will see in fact they are not stoped (from console point view)
# for that reason we check listener_stop on Mouse Move and on Keyboard Key press.
# If for some reason the app is quit and listener has not stoped then on first action did will stop
# Mouse move after app quit >> Mouse listener will stop
# Keyboard button pressed after app quit >> Keyboard listener will stops
if listener_stop == False:
listener_stop = True
print("Events Listening are stoped!")
master.destroy()
# // Run if this is tha main file
if __name__ == "__main__":
app:object = MainApp(root)
root.protocol("WM_DELETE_WINDOW", app.SafeQuit)
root.mainloop()
我再次更新了代码,停止监听键盘和鼠标事件。
PS:它们的更新版本可以是found on my github