Search code examples
pythonpython-3.xpynput

using pynput for key events instead of tkinter


Here is my entire program. I want to change from using tkinter with a gui, to using pynput no gui for get_key events part of my code. Can anyone show me what code to use? This code is a talking vending machine that reads from the machine contents list, which is a file that gets updated by the vending machine company.

I dont want to use a gui as there will be no monitor attached. It's a Raspberry.

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()

Solution

  • I edited this comment because after I played with OpenGL and Pygame I found the answer of how to use pynput with tkinter.

    This is the code sample what I write it to test if works.

    # // 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()
    

    I updated the code again with stop lisening keyboard and mouse event.

    PS: An updated version of them can be found on my github