python 按键精灵脚本_[620]使用Python实现一个按键精灵

按键精灵想必很多人都玩过,使用录制功能将鼠标和键盘的操作录制下来,录制好后就可以通过回放自动执行之前录制的操作,可以设置重复执行的次数,这样就可以将一些重复的劳动交给脚本自动化去完成。使用Python编写一个和脚本精灵一样的程序非常简单,并且代码量足够少,好处是可以不再依赖按键精灵,像我这种有轻微洁癖的程序猿就喜欢自己动手实现,依赖Python的为所欲为的特性,可以任意编码让自己的按键精灵更加强大。

按键精灵的实现可以拆解分为录制和回放两个步骤,对应到Python程序的实现也可以分为两步:

1.监听鼠标键盘的事件和坐标,写入到文件中记录起来。

2.读取监听时写入的文件,执行文件中的坐标和事件操作。

要实现这两个功能,得先从基础开始。我把任务拆解成两大部分,鼠标和键盘属于不同的两个输入设备,所以分开实现“鼠标精灵”和“键盘精灵”两个程序,最后融合这两个模块实现一个相对完整的按键精灵。

鼠标事件监听from pynput import mouse

# 鼠标移动事件

def on_move(x, y):

print('[Move]', (x, y))

# 鼠标点击事件

def on_click(x, y, button, pressed):

print('[Click]', (x, y, button.name, pressed))

# 鼠标滚动事件

def on_scroll(x, y, x_axis, y_axis):

print('[Scroll]', (x, y, x_axis, y_axis))

# 监听事件绑定

with mouse.Listener(on_move=on_move, on_click=on_click, on_scroll=on_scroll) as listener:

listener.join()

onMove(x,y)函数接收鼠标当前的x轴和y轴坐标,启动程序并移动鼠标时,就会调用该方法。

on_click(x, y, button, pressed)函数接收鼠标的点击事件,x和y为当前点击事件的鼠标坐标,button参数对象的name属性值为left或者right,通过该属性值可以判断是鼠标的左键还是右键产生的点击事件,pressed参数值为True时表示当前鼠标左或右键按压,False时表示鼠标左或右键抬起事件。

on_scroll(x, y, x_axis, y_axis)接收四个参数,前两个参数依旧是当前事件的鼠标坐标轴,x_axis的值>0表示向上,<0表示向下,同样的y_axis的负值和正值代表左滑和右滑状态。

鼠标事件执行from pynput.mouse import Button, Controller

import time

# 获取鼠标对象

mouse = Controller()

# 输出鼠标当前的坐标

print(mouse.position)

# 将新的坐标赋值给鼠标对象

mouse.position = (100, 500)

for index in range(0, 30):

# 鼠标移动到指定坐标轴

mouse.move(index, -index)

print(mouse.position)

time.sleep(0.01)

for index in range(0, 30):

# 鼠标移动到指定坐标轴

mouse.move(-index, index)

print(mouse.position)

time.sleep(0.01)

# 鼠标右键按下

mouse.press(Button.right)

time.sleep(0.01)

# 鼠标右键抬起

mouse.release(Button.right)

# 鼠标左键点击

mouse.click(Button.left, 1)

# 鼠标滚轮滚动距离500

mouse.scroll(0, 500)

和鼠标事件监听一样,对应的我们可以操作鼠标的各种事件:移动、左/右按压、左/右抬起、左/右点击、上下左右滚动

上面代码中的mouse.move(x,y)函数,表示从当前鼠标位置进行位移的距离,x和y的值是以当前位置为0点开始算的。

而且不能简单的用坐标轴去相减得到位移距离,所以后续的程序我会使用mouse.position = (x, y)这个函数来操作鼠标的移动,

这个函数可以将鼠标设置到指定位置,只要我们记录之前的鼠标移动轨迹,就可以通过读取之前的记录文件按顺序重新对鼠标进行赋值操作。达到回放的效果。

带录制回放功能的鼠标精灵

结合鼠标事件的监听和执行,并且将事件记录到文件中,再加上Python自带的GUI,就可以写出一个简单的鼠标精灵了。

实现思路:定义一个json格式的对象来统一不同鼠标事件的内容格式{

"name":"mouse",

"event":"click",

"target":"left",

"action":true,

"location":{

"x":"0",

"y":"0"

}

}

鼠标事件的name值为mouse,考虑到后续还有可能会有其他设备的事件,比如键盘事件。

鼠标的事件为点击事件,将event赋值为click。

target表示目标,点击了鼠标左键,所以目标值为left

action表示动作,鼠标点击分为按压和抬起,true表示抬起。

location的值包含一个json对象,里面为当前事件鼠标的x和y坐标。

鼠标的移动和滑动事件以此类推,用同样的模板格式进行记录。将记录的数据写入到文件中

执行回放时通过逐行读取文件,解析文件中的json数据通过name、event、location等值进行事件执行。

之所以选择json时因为解析起来比较方便。当然你也可以使用数据库,比如SQLite等,或者自己定义一套自己的格式。

json的存储方式有太多的冗余字符了,空间占用目前不是考虑的首要因素,这里先用最快的方式实现它。

GUI用了Python自带的tkinter库,为了防止UI阻塞,所以使用了多线程,同样是Python自带库threading。

整个实现其实只用了一个三方库 pynput

鼠标事件的监听录制和执行回放的完整代码如下:import json

import threading

import time

import tkinter

from pynput import mouse

from pynput.mouse import Button, Controller

# 鼠标动作模板

def mouse_action_template():

return {

"name": "mouse",

"event": "default",

"target": "default",

"action": "default",

"location": {

"x": "0",

"y": "0"

}

}

# 鼠标动作监听

class MouseActionListener(threading.Thread):

def __init__(self, file_name):

super().__init__()

self.file_name = file_name

def run(self):

with open(self.file_name, 'w', encoding='utf-8') as file:

# 鼠标移动事件

def on_move(x, y):

template = mouse_action_template()

template['event'] = 'move'

template['location']['x'] = x

template['location']['y'] = y

file.writelines(json.dumps(template) + "\n")

file.flush()

# 鼠标点击事件

def on_click(x, y, button, pressed):

template = mouse_action_template()

template['event'] = 'click'

template['target'] = button.name

template['action'] = pressed

template['location']['x'] = x

template['location']['y'] = y

file.writelines(json.dumps(template) + "\n")

file.flush()

# 鼠标滚动事件

def on_scroll(x, y, x_axis, y_axis):

template = mouse_action_template()

template['event'] = 'scroll'

template['location']['x'] = x_axis

template['location']['y'] = y_axis

file.writelines(json.dumps(template) + "\n")

file.flush()

with mouse.Listener(on_move=on_move, on_click=on_click, on_scroll=on_scroll) as listener:

listener.join()

# 鼠标动作执行

class MouseActionExecute(threading.Thread):

def __init__(self, file_name):

super().__init__()

self.file_name = file_name

def run(self):

with open(self.file_name, 'r', encoding='utf-8') as file:

mouse_exec = Controller()

line = file.readline()

time.sleep(0.01)

while line:

obj = json.loads(line)

if obj['name'] == 'mouse':

if obj['event'] == 'move':

mouse_exec.position = (obj['location']['x'], obj['location']['y'])

time.sleep(0.01)

elif obj['event'] == 'click':

if obj['action']:

if obj['target'] == 'left':

mouse_exec.press(Button.left)

else:

mouse_exec.press(Button.right)

else:

if obj['target'] == 'left':

mouse_exec.release(Button.left)

else:

mouse_exec.release(Button.right)

time.sleep(0.01)

elif obj['event'] == 'scroll':

mouse_exec.scroll(obj['location']['x'], obj['location']['y'])

time.sleep(0.01)

line = file.readline()

def button_onClick(action):

m1 = MouseActionListener(file_name='mouse.action')

m2 = MouseActionExecute(file_name='mouse.action')

if action == 'listener':

if startListenerBtn['text'] == '录制':

m1.start()

startListenerBtn['text'] = '录制中...关闭程序停止录制'

startListenerBtn['state'] = 'disabled'

elif action == 'execute':

if startExecuteBtn['text'] == '回放':

m2.start()

startExecuteBtn['text'] = '回放中...关闭程序停止回放'

startExecuteBtn['state'] = 'disabled'

if __name__ == '__main__':

root = tkinter.Tk()

root.title('鼠标精灵-蓝士钦')

root.geometry('200x200+400+100')

startListenerBtn = tkinter.Button(root, text="录制", command=lambda: button_onClick('listener'))

startListenerBtn.place(x=10, y=10, width=180, height=80)

startExecuteBtn = tkinter.Button(root, text="回放", command=lambda: button_onClick('execute'))

startExecuteBtn.place(x=10, y=110, width=180, height=80)

root.mainloop()

按键精灵的鼠标部分到这里就基本完成了。

运行程序,点击录制,然后就可以用你的鼠标在屏幕上一顿操作。然后关闭本程序。

接着重新打开程序,点击回放,就会发现鼠标可以按照之前录制的动作进行自动工作了。

记住千万不要在录制时,还没关闭程序的时候就点击回放,这样会陷入无限循环里面,会导致不停的录制不停的回放。

还有键盘的程序后续补上,程序待完善中,未完待续。

键盘事件监听from pynput import keyboard

# 按键按下监听

def on_press(key):

try:

print('press key {0}, vk: {1}'.format(key.char, key.vk))

except AttributeError:

print('special press key {0}, vk: {1}'.format(key, key.value.vk))

# 按键释放监听

def on_release(key):

if key == keyboard.Key.esc:

# 停止监听

return False

try:

print('release key {0}, vk: {1}'.format(key.char, key.vk))

except AttributeError:

print('special release key {0}, vk: {1}'.format(key, key.value.vk))

# 键盘监听

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:

listener.join()

键盘监听相对于鼠标监听来说,回调的函数只有两个on_press按键按下 和on_release按键释放。

由于pynput这个库对键盘的不同按键事件封装进行了区分,比如普通的数字和字母键按下会进入on_press方法,通过传入的key.char属性值可以得到按键对应在键盘上的字符,但如果是Shift等其他特殊键,就没有char属性,会产生异常。

只要捕获异常后直接通过key就可以取到特殊键对应的字符。我觉得这是pynput做得有点不够优雅的地方。

普通的键有key.vk属性值,代表键盘上字符对应的编码值,特殊键的编码值要通过key.value.vk来取。

键盘事件执行from pynput.keyboard import Key, Controller, KeyCode

# 键盘控制对象

keyboard = Controller()

# 按下 a 键

keyboard.press('a')

# 释放 a 键

keyboard.release('a')

# 按下 Shift 键

keyboard.press(Key.shift)

keyboard.press('b')

keyboard.release('b')

keyboard.press('c')

keyboard.release('c')

# 释放 Shift 键

keyboard.release(Key.esc)

# 按下 Shift 键,然后依次按下其他按键,完成后Shift键自动释放

with keyboard.pressed(Key.shift):

keyboard.press('d')

keyboard.release('d')

keyboard.press('e')

keyboard.release('e')

# 依次按下 python (包括前面的空格)

keyboard.type(' python')

# 按下 vk值为56的键 shift 键

keyboard.touch(KeyCode.from_vk(56), True)

keyboard.touch('a', True)

keyboard.touch('a', False)

# 释放 shift 键

keyboard.touch(Key.shift, False)

和监听方法对应,执行键盘的按键方法有press(key)按压 release(key)释放。

除此之外还有touch(key,is_press)函数,key表示要操作的键,is_press 为True时表示按压,为False时表示释放。

无论是哪种按压事件,Key都可以通过其他方式构造,比如知道Shift的vk值为56,那么就可以通过KeyCode.from_vk(56)来构造一个Shift的Key。

通过VK编码构造Key的方式很有用,因为当你要按出一个@符号时,需要同时按住Shift和2。通过on_press(key)监听得到的值是一个@符号,如果录制程序录制了一个@符号将无法通过keyboard.press('@')这种方式直接执行。

所以接下来的键盘录制回放程序我将通过定义一个键盘动作模板,然后通过VK值准确的记录每个键以及每个组合键的编码。然后通过keyboard.press(KeyCode.from_vk(vk))进行回放。

带录制回放功能的键盘精灵import json

import threading

import time

import tkinter

from pynput import keyboard

from pynput.keyboard import Controller, KeyCode

# 键盘动作模板

def keyboard_action_template():

return {

"name": "keyboard",

"event": "default",

"vk": "default"

}

# 键盘动作监听

class KeyboardActionListener(threading.Thread):

def __init__(self, file_name):

super().__init__()

self.file_name = file_name

def run(self):

with open(self.file_name, 'w', encoding='utf-8') as file:

# 键盘按下监听

def on_press(key):

template = keyboard_action_template()

template['event'] = 'press'

try:

template['vk'] = key.vk

except AttributeError:

template['vk'] = key.value.vk

finally:

file.writelines(json.dumps(template) + "\n")

file.flush()

# 键盘抬起监听

def on_release(key):

if key == keyboard.Key.esc:

# 停止监听

startListenerBtn['text'] = '录制'

startListenerBtn['state'] = 'normal'

return False

template = keyboard_action_template()

template['event'] = 'release'

try:

template['vk'] = key.vk

except AttributeError:

template['vk'] = key.value.vk

finally:

file.writelines(json.dumps(template) + "\n")

file.flush()

# 键盘监听

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:

listener.join()

# 键盘动作执行

class KeyboardActionExecute(threading.Thread):

def __init__(self, file_name):

super().__init__()

self.file_name = file_name

def run(self):

with open(self.file_name, 'r', encoding='utf-8') as file:

keyboard_exec = Controller()

line = file.readline()

time.sleep(3)

while line:

obj = json.loads(line)

if obj['name'] == 'keyboard':

if obj['event'] == 'press':

keyboard_exec.press(KeyCode.from_vk(obj['vk']))

time.sleep(0.01)

elif obj['event'] == 'release':

keyboard_exec.release(KeyCode.from_vk(obj['vk']))

time.sleep(0.01)

line = file.readline()

startExecuteBtn['text'] = '回放'

startExecuteBtn['state'] = 'normal'

def button_onClick(action):

m1 = KeyboardActionListener(file_name='keyboard.action')

m2 = KeyboardActionExecute(file_name='keyboard.action')

if action == 'listener':

if startListenerBtn['text'] == '录制':

m1.start()

startListenerBtn['text'] = '录制中...esc停止录制'

startListenerBtn['state'] = 'disabled'

elif tener'))

startListenerBtn.place(x=10, y=10, width=180, height=80)

startExecuteBtn = tkinter.Button(root, text="回放", command=lambda: button_onClick('execute'))

startExecuteBtn.place(x=10, y=110, width=180, height=80)

root.mainloop()

键盘精灵的录制和回放程序到这里以及算是一个基础版本了,可以正常使用。并且新增了esc键监听,当用户点击esc时将会结束录制。

鼠标精灵和键盘精灵都可以单独的运行使用。

大多数场景下这两者的功能都会使用到,所以接下来我要实现一个完成的按键精灵,同时包含鼠标和键盘的录制回放功能。

在之前的代码基础上进一步封装。

考虑的基本要素如下:记录鼠标和记录键盘的事件采用不同的json模板进行定义,采用响应式对用户的操作进行监听,用户静止不动则不会写入文件。

同时监听鼠标和键盘,为了避免多线程写同一个文件的锁操作,我将鼠标和键盘的录制记录分为两个不同的文件。

录制和回放的操作通常都需要有一个等待时间的设置,所以代码里加上了GUI的设置部分,GUI没有设计所以后续这块代码要优化。

考虑录制和回放倒计时需要UI提示用户并且定时触发线程执行,所以封装了一个UI更新线程的类。

按键精灵0.1版本完整代码如下:

键鼠录制的按键精灵0.1版本import json

import threading

import time

import tkinter

from pynput import keyboard, mouse

from pynput.keyboard import Controller as KeyBoardController, KeyCode

from pynput.mouse import Button, Controller as MouseController

# 键盘动作模板

def keyboard_action_template():

return {

"name": "keyboard",

"event": "default",

"vk": "default"

}

# 鼠标动作模板

def mouse_action_template():

return {

"name": "mouse",

"event": "default",

"target": "default",

"action": "default",

"location": {

"x": "0",

"y": "0"

}

}

# 倒计时监听,更新UI触发自定义线程对象

class UIUpdateCutDownExecute(threading.Thread):

def __init__(self, cut_down_time, custom_thread_list):

super().__init__()

self.cut_down_time = cut_down_time

self.custom_thread_list = custom_thread_list

def run(self):

while self.cut_down_time > 0:

for custom_thread in self.custom_thread_list:

if custom_thread['obj_ui'] is not None:

custom_thread['obj_ui']['text'] = str(self.cut_down_time)

custom_thread['obj_ui']['state'] = 'disabled'

self.cut_down_time = self.cut_down_time - 1

time.sleep(1)

else:

for custom_thread in self.custom_thread_list:

if custom_thread['obj_ui'] is not None:

custom_thread['obj_ui']['text'] = custom_thread['final_text']

custom_thread['obj_ui']['state'] = 'disabled'

if custom_thread['obj_thread'] is not None:

custom_thread['obj_thread'].start()

time.sleep(1)

# 键盘动作监听

class KeyboardActionListener(threading.Thread):

def __init__(self, file_name='keyboard.action'):

super().__init__()

self.file_name = file_name

def run(self):

with open(self.file_name, 'w', encoding='utf-8') as file:

# 键盘按下监听

def on_press(key):

template = keyboard_action_template()

template['event'] = 'press'

try:

template['vk'] = key.vk

except AttributeError:

template['vk'] = key.value.vk

finally:

file.writelines(json.dumps(template) + "\n")

file.flush()

# 键盘抬起监听

def on_release(key):

if key == keyboard.Key.esc:

# 停止监听

startListenerBtn['text'] = '开始录制'

startListenerBtn['state'] = 'normal'

keyboardListener.stop()

return False

template = keyboard_action_template()

template['event'] = 'release'

try:

template['vk'] = key.vk

except AttributeError:

template['vk'] = key.value.vk

finally:

file.writelines(json.dumps(template) + "\n")

file.flush()

# 键盘监听

with keyboard.Listener(on_press=on_press, on_release=on_release) as keyboardListener:

keyboardListener.join()

# 键盘动作执行

class KeyboardActionExecute(threading.Thread):

def __init__(self, file_name='keyboard.action', execute_count=0):

super().__init__()

self.file_name = file_name

self.execute_count = execute_count

def run(self):

while self.execute_count > 0:

with open(self.file_name, 'r', encoding='utf-8') as file:

keyboard_exec = KeyBoardController()

line = file.readline()

while line:

obj = json.loads(line)

if obj['name'] == 'keyboard':

if obj['event'] == 'press':

keyboard_exec.press(KeyCode.from_vk(obj['vk']))

time.sleep(0.01)

elif obj['event'] == 'release':

keyboard_exec.release(KeyCode.from_vk(obj['vk']))

time.sleep(0.01)

line = file.readline()

startExecuteBtn['text'] = '开始回放'

startExecuteBtn['state'] = 'normal'

self.execute_count = self.execute_count - 1

# 鼠标动作监听

class MouseActionListener(threading.Thread):

def __init__(self, file_name='mouse.action'):

super().__init__()

self.file_name = file_name

def run(self):

with open(self.file_name, 'w', encoding='utf-8') as file:

# 鼠标移动事件

def on_move(x, y):

template = mouse_action_template()

template['event'] = 'move'

template['location']['x'] = x

template['location']['y'] = y

file.writelines(json.dumps(template) + "\n")

file.flush()

# 鼠标点击事件

def on_click(x, y, button, pressed):

template = mouse_action_template()

template['event'] = 'click'

template['target'] = button.name

template['action'] = pressed

template['location']['x'] = x

template['location']['y'] = y

file.writelines(json.dumps(template) + "\n")

file.flush()

# 鼠标滚动事件

def on_scroll(x, y, x_axis, y_axis):

template = mouse_action_template()

template['event'] = 'scroll'

template['location']['x'] = x_axis

template['location']['y'] = y_axis

file.writelines(json.dumps(template) + "\n")

file.flush()

with mouse.Listener(on_move=on_move, on_click=on_click, on_scroll=on_scroll) as mouseListener:

mouseListener.join()

# 鼠标动作执行

class MouseActionExecute(threading.Thread):

def __init__(self, file_name='mouse.action', execute_count=0):

super().__init__()

self.file_name = file_name

self.execute_count = execute_count

def run(self):

while self.execute_count > 0:

with open(self.file_name, 'r', encoding='utf-8') as file:

mouse_exec = MouseController()

line = file.readline()

while line:

obj = json.loads(line)

if obj['name'] == 'mouse':

if obj['event'] == 'move':

mouse_exec.position = (obj['location']['x'], obj['location']['y'])

time.sleep(0.01)

elif obj['event'] == 'click':

if obj['action']:

if obj['target'] == 'left':

mouse_exec.press(Button.left)

else:

mouse_exec.press(Button.right)

else:

if obj['target'] == 'left':

mouse_exec.release(Button.left)

else:

mouse_exec.release(Button.right)

time.sleep(0.01)

elif obj['event'] == 'scroll':

mouse_exec.scroll(obj['location']['x'], obj['location']['y'])

time.sleep(0.01)

line = file.readline()

def command_adapter(action):

if action == 'listener':

if startListenerBtn['text'] == '开始录制':

custom_thread_list = [

{

'obj_thread': KeyboardActionListener(),

'obj_ui': startListenerBtn,

'final_text': '录制中...esc停止录制'

},

{

'obj_thread': MouseActionListener(),

'obj_ui': None,

'final_text': None

}

]

UIUpdateCutDownExecute(startTime.get(), custom_thread_list).start()

elif action == 'execute':

if startExecuteBtn['text'] == '开始回放':

custom_thread_list = [

{

'obj_thread': KeyboardActionExecute(execute_count=playCount.get()),

'obj_ui': startExecuteBtn,

'final_text': '回放中...关闭程序停止回放'

},

{

'obj_thread': MouseActionExecute(execute_count=playCount.get()),

'obj_ui': None,

'final_text': None

}

]

UIUpdateCutDownExecute(endTime.get(), custom_thread_list).start()

def isNumber(content):

if content.isdigit() or content == "":

return True

else:

return False

if __name__ == '__main__':

root = tkinter.Tk()

root.title('按键精灵-蓝士钦')

root.geometry('200x200+400+100')

listenerStartLabel = tkinter.Label(root, text='录制倒计时')

listenerStartLabel.place(x=10, y=10, width=80, height=20)

startTime = tkinter.IntVar()

listenerStartEdit = tkinter.Entry(root, textvariable=startTime)

listenerStartEdit.place(x=100, y=10, width=60, height=20)

startTime.set(3)

listenerTipLabel = tkinter.Label(root, text='秒')

listenerTipLabel.place(x=160, y=10, width=20, height=20)

startListenerBtn = tkinter.Button(root, text="开始录制", command=lambda: command_adapter('listener'))

startListenerBtn.place(x=10, y=45, width=180, height=30)

executeEndLabel = tkinter.Label(root, text='回放倒计时')

executeEndLabel.place(x=10, y=85, width=80, height=20)

endTime = tkinter.IntVar()

executeEndEdit = tkinter.Entry(root, textvariable=endTime)

executeEndEdit.place(x=100, y=85, width=60, height=20)

endTime.set(6)

executeTipLabel = tkinter.Label(root, text='秒')

executeTipLabel.place(x=160, y=85, width=20, height=20)

playCountLabel = tkinter.Label(root, text='回放次数')

playCountLabel.place(x=10, y=115, width=80, height=20)

playCount = tkinter.IntVar()

playCountEdit = tkinter.Entry(root, textvariable=playCount)

playCountEdit.place(x=100, y=115, width=60, height=20)

playCount.set(1)

playCountTipLabel = tkinter.Label(root, text='次')

playCountTipLabel.place(x=160, y=115, width=20, height=20)

startExecuteBtn = tkinter.Button(root, text="开始回放", command=lambda: command_adapter('execute'))

startExecuteBtn.place(x=10, y=145, width=180, height=30)

root.mainloop()

脚本精灵0.1版本完成?

还需要考虑的点:键盘事件没有记录用户每个动作之间的延迟时间,无法准确重放用户的输入节奏,后续考虑记录时间间隔点。

鼠标事件用户移动的越快,产生的点位变化也就越频繁,所以鼠标在回放时的速度与用户的操作基本一致。

鼠标没有停止回放的快捷键,要考虑如何停止回放鼠标事件。

输入法切换可能导致重放键盘按键时输入不准确,需要录制时是什么输入状态,重放时也要对应的键盘属性和状态

…还有很多需要考虑的点(原本只是想简单的做个示例程序)

程序只在MacOS平台上实验过,其他平台还未实验,一个相对完整的按键精灵在录制时应该获取更多的信息,这样在回放的时候才足够准确,后续考虑做一个更加精确的按键精灵,比如加入获取屏幕像素点,回放时通过采样比对,达到为所欲为功能。

GitHub地址:

https://github.com/lanshiqin/JerryMouse

期待大家来一起完善它?

来源:https://www.lanshiqin.com/2fb233e2/

你可能感兴趣的:(python,按键精灵脚本)