无法从变量中获取键盘键

Cannot get keyboard key from variable

所以我正在尝试制作一个可以 hold/autoclick 键盘按键的程序。 问题是,程序不理解我给它的变量(从 Tkinter Entry 获得)要按下的键。

当我用pynput按键时,提示我没有给它赋值:

AttributeError: 'str' object has no attribute 'value'

当我使用 pyautogui 按键时,它什么也没做,甚至 return 都没有报错。

部分代码无效:

        #Autoclicking keyboard key (using pynput) (not working)
        #hold = whether to autoclick or hold key
        #clkdel = delay between keyboard presses
        #keyprsd = key to be pressed (tk.StringVar)
        #lf7 = listener for when to start/stop
        if int(hold.get()) == 1:
            print('Starting keyboard pressing...')
            while self.run == True:
                print('Pressed')
                master.after(clkdel, keyct.press(keyprsd.get()))
            lf7.stop()

完整代码:

import tkinter as tk
from pynput.keyboard import Key, Listener
from pynput.mouse import Button, Controller
from pynput import keyboard
import pyautogui

class GUI:
   def __init__(self, master):
       #Defining variables
       hold = tk.IntVar()
       keyprsd = tk.StringVar()
       self.run = False
       msbt = tk.IntVar()
       mousect = Controller()
       keyct = Controller()
       #Creating main window
       master = master
       master.title('Key Clicker')
       master.geometry('250x250')
       master.resizable(False, False)
       #Creating radio buttons:
       #Autoclick button
       self.autoclick = tk.Radiobutton(master, text='Autoclick', variable=hold, value=1)
       self.autoclick.grid(row=0, column=0, sticky='en', padx=30, pady=5)
       #Hold button
       self.holdbt = tk.Radiobutton(master, text='Hold', variable=hold, value=2)
       self.holdbt.grid(row=0, column=1, sticky='wn', padx=0, pady=5)
       #Creating keyboard button label
       self.kbtlabel = tk.Label(master, textvariable=keyprsd, width=10, bg='Light Blue')
       self.kbtlabel.grid(row=1, column=0, sticky='wens')
       #Creating keyboard button detection:
       #Functions to detect key to be pressed
       def lforsetkey():
           lst = Listener(on_press=ksetcall)
           lst.start()
       def ksetcall(key):
           print('{} was pressed'.format(key))
           keyprsd.set(key)
           return False
       #Functions to detect when to start and stop autoclicking/holding
       def f7press(key):
           print('{} was pressed'.format(key))
           if key == keyboard.Key.f7:
               self.run = False
           if key == keyboard.Key.f6:
               self.run = True
               stmouse()
           if key == keyboard.Key.f8:
               self.run = True
               stkey()
       def lforf7():
           global lf7
           lf7 = Listener(on_press=f7press)
           lf7.start()
       lforf7()
       #Creating key selection button
       self.kbtsel = tk.Button(master, text='Click and press a key', command=lforsetkey)
       self.kbtsel.grid(row=2, column=0, sticky='wen', padx=0)
       #Creating list for mouse buttons
       self.mslft = tk.Radiobutton(master, text='Left Click', variable=msbt, value=1)
       self.mslft.grid(row=1, column=1, sticky='en', padx=10)
       self.msrft = tk.Radiobutton(master, text='Right Click', variable=msbt, value=2)
       self.msrft.grid(row=2, column=1, sticky='en', padx=10)
       #Creating autoclick frequency label
       self.clklb = tk.Label(master, text='Autoclick frequency (ms)')
       self.clklb.grid(row=3, column=0)
       #Creating autoclick frequency entry
       self.clkent = tk.Entry(master)
       self.clkent.grid(row=3, column=1)
       #Creating mouse autoclick button
       def stmouse():
           self.run = True
           lforf7()
           try:
               clkdel = int(self.clkent.get())
           except ValueError:
               print('Value Error')
           #Autoclicking left button
           if int(msbt.get()) == 1 and int(hold.get()) == 1:
               print('Starting mouse autoclick...')
               while self.run == True:
                   print('Clicked')
                   master.after(clkdel, mousect.click(Button.left))
               lf7.stop()
           #Autoclicking right button
           elif int(msbt.get()) == 2 and int(hold.get()) == 1:
               print('Starting mouse autoclick...')
               while self.run == True:
                   print('Clicked')
                   master.after(clkdel, mousect.click(Button.right))
               lf7.stop()
           #Holding left button
           elif int(msbt.get()) == 1 and int(hold.get()) == 2:
               print('Starting mouse holding...')
               while self.run == True:
                   pyautogui.mouseDown(button='left')
               lf7.stop()
               pyautogui.mouseUp(button='left')
           #Holding right button
           elif int(msbt.get()) == 2 and int(hold.get()) == 2:
               print('Starting mouse holding...')
               while self.run == True:
                   pyautogui.mouseDown(button='right')
               lf7.stop()
               pyautogui.mouseUp(button='right')
           else:
               print('Error')
       self.msbt = tk.Button(master, text='Click to start holding/autoclicking mouse', command=stmouse)
       self.msbt.grid(row=4, column=0, columnspan=2, sticky='swe', pady=5)
       #Creating keyboard button
       def stkey():
           #Setting up variables and starting listener
           self.run = True
           lforf7()
           try:
               clkdel = int(self.clkent.get())
           except ValueError:
               print('Value Error')
           #Autoclicking keyboard key (using pynput) (not working)
           #hold = whether to autoclick or hold key
           #clkdel = delay between keyboard presses
           #keyprsd = key to be pressed (tk.StringVar)
           #lf7 = listener for when to start/stop
           if int(hold.get()) == 1:
               print('Starting keyboard pressing...')
               while self.run == True:
                   print('Pressed')
                   master.after(clkdel, keyct.press(keyprsd.get()))
               lf7.stop()
           #Holding keyboard key (also not working)
           elif int(hold.get()) == 2:
               print('Holding keyboard...')
               while self.run == True:
                   pyautogui.keyDown(keyprsd.get())
               lf7.stop()
               print('Stopping')
               pyautogui.keyUp(keyprsd.get())
           else:
               print('Error')
       self.kbt = tk.Button(master, text='Click to start holding/autoclicking keyboard', command=stkey)
       self.kbt.grid(row=5, column=0, columnspan=2, sticky='swe', pady=5)
root = tk.Tk()
GUI(root)
root.mainloop()

你的主要问题是你只导入了鼠标控制器

   from pynput.mouse import Button, Controller

你用它来做鼠标和键盘

   mousect = Controller()
   keyct = Controller()

但是他们有不同的控制器,它们的工作方式不同。

键盘控制器可以获取字符串,但鼠标控制器需要带有 .value

的对象

你应该使用 differenet 控制器

   from pynput import mouse
   from pynput import keyboard

   mousect = mouse.Controller()
   keyct = keyboard.Controller()

这解决了主要问题。


但还有其他问题 - after()(类似于 command=bind())需要没有参数的函数名称,您可以使用 lambda(如 command=bind()) 或者你必须在函数名后使用参数

   master.after(clkdel, keyct.press, keyprsd.get())

   master.after(clkdel, mousect.click, Button.left)

   master.after(clkdel, mousect.click, Button.right)

顺便说一句: 另一个问题是您的代码不可读。您应该将嵌套函数移到 class 之外或创建 class 方法,这样您就不需要 global 而需要 self.。您还可以使用更具可读性的名称 - 即。 key_controllerkeyct.

更具可读性

参见:PEP 8 -- Style Guide for Python Code


编辑:

Listener 为特殊对象提供有关密钥的信息,然后将其作为字符串放入 StringVar 中,稍后您从 StringVar 中获取它作为字符串并在 [=30 中使用该字符串=] - 它可以用于普通键但不适用于特殊键。

您必须保留原始对象并在 press() 中将此对象用于所有键(普通键和特殊键)。即

   def ksetcall(key):

       self.key = key   # keep original object 

       print('{} was pressed'.format(key))
       keyprsd.set(key)
       return False

以后

 keyct.press(self.key)

而不是keyct.press(keyprsd.get())