前言
Python 中提供了很多模块可以用于控制输入设备,像是传统的 win32gui,或者是用于游戏开发的 Pygame。其中 win32gui 更贴切的说是基于 Windows 的编程,它的操作丰富多样,可以获取每个窗口,也可以获取窗口的题柄等。而 Pygame 的长处在于 2D 游戏的开发。而今天要讲的 pynput 则不同,它操作非常简单,而且里面包含的内容也更贴切输入设备,其中非常重要的两个模块就是 mouse 和 keyboard,分别提供了控制鼠标和键盘的类,下面我们就来看看一些具体操作。
1. 控制鼠标
我们先来安装这个模块,安装起来非常简单,我们直接使用 pip 安装:
pip install pynput
接下来就可以使用该模块了。我们导入 mouse 模块:
from pynput import mouse
在 mouse 模块中提供了一个 Controller 类,该类就是我们的鼠标控制器,我们创建该类的对象就可以鼠标键盘:
from pynput import mouse
# 创建一个鼠标
m = mouse.Controller()
获取了鼠标对象后,我们就可以获取一些属性,或者进行一些操作。
1.1 获取鼠标位置
我们可以获取鼠标的位置信息,也就是当前鼠标所在的坐标:
from pynput import mouse
# 创建一个鼠标
m = mouse.Controller()
# 输出鼠标的位置
print(m.position)
输出结果为一个元组。
1.2 定位鼠标
我们也可以直接修改鼠标的位置:
from pynput import mouse
# 创建鼠标
m = mouse.Controller()
# 将鼠标移动到左上角
m.position = (0, 0)
这种方式是直接定位鼠标,我们还可以根据当前位置移动鼠标。
1.3 移动鼠标
移动鼠标调用的是 move 函数:
from pynput import mouse
# 创建鼠标
m = mouse.Controller()
# 将鼠标移动到左上角
m.move(50, -50)
第一个参数为 x 移动的值,第二个参数为 y 移动的值。
另外一般鼠标上都会有三个控制按钮,左键、右键和滚轮,下面我们看看如何操作它们。
1.4 点击鼠标
我们点击按钮时都会先按下按钮,然后再松开按钮:
from pynput import mouse
# 创建鼠标
m = mouse.Controller()
# 按下鼠标右键
m.press(mouse.Button.right)
# 松下鼠标右键
m.release(mouse.Button.right)
在 mouse 提供了 Button 类,里面内置了左键和右键的常量,我们直接使用就可以了。
除了上面的方法,我们还可以直接调用 click 方法,点击鼠标:
from pynput import mouse
# 创建鼠标
m = mouse.Controller()
# 点击鼠标左键
m.click(mouse.Button.left)
1.5 双击鼠标
双击也是个非常常用的操作,我们同样可以使用 click 方法:
from pynput import mouse
# 创建鼠标
m = mouse.Controller()
# 点击鼠标左键
m.click(mouse.Button.left, 2)
click 方法接收两个参数,第一个为按钮,第二个为非必选参数,含义为点击的次数。
1.6 滚动滚轮
对于像 Excel 表这种大型的表格,我们经常需要上下左右滚动,而 mouse 模块中就提供了这样的方法:
from pynput import mouse
# 创建鼠标
m = mouse.Controller()
# 滚动鼠标,第一个参数为 y 滚动的数值,第二个参数为 x 滚动的数值
m.scroll(0, -10)
1.7 监听鼠标的事件
鼠标中的事件有三个,点击事件、移动事件、滚动事件,我们看看如何监听鼠标的事件:
from pynput import mouse
def on_move(x, y):
"""鼠标移动的监听方法 x,y 为移动后的位置"""
print('鼠标移动到了{0}'.format((x, y)))
def on_click(x, y, button, pressed):
"""鼠标点击的监听方法 x,y 为坐标,button 为按钮,pressed 为是否是按下"""
if pressed:
print('点击了({0}, {1})'.format(x, y))
else:
print('鼠标在({0}, {1})松开'.format(x, y))
def on_scroll(x, y, dx, dy):
"""鼠标滚动的监听方法 x,y 为作为,dx,dy 为滚动幅度"""
print('鼠标在{0}, 向右滚动{1}, 向下滚动{2}'.format((x, y), dx, dy))
# 创建一个监听者
with mouse.Listener(
# 关联监听方法(不加括号)
on_move=on_move,
on_click=on_click,
on_scroll=on_scroll) as listener:
# 阻塞线程
listener.join()
我们的 mouse 模块提供了 Listener 类,该类的对象就是我们的监听者。当我们触发某个事件时,监听者就会执行关联好的方法。
2. 控制键盘
在 pynput 中提供了 keyboard 模块,该模块中提供了与 mouse 模块类似的一些类,这些类可以用于控制键盘。其中 keyboard 中也有一个 Controller 类,该类对象就是我们的键盘控制器。
from pynput import keyboard
# 创建一个键盘
kb = keyboard.Controller()
我们可以通过上述代码创建一个键盘控制器。有了控制器我们就可以操作这个键盘了。
2.1 按下并松开某个键
这里同样是调用 press 和 release 方法:
from pynput import keyboard
# 创建一个键盘
kb = keyboard.Controller()
# 按下 a 键
kb.press('a')
# 松开 a 键
kb.release('a')
上面我们是通过传入字符的方式按按钮,这里智能点击单个字符的按钮。在 keyboard 模块中 Key 类中,提供了大量预设的按钮,我们可以直接使用:
from pynput import keyboard
# 创建键盘
kb = keyboard.Controller()
# 按下大小写锁定
kb.press(keyboard.Key.caps_lock)
# 松开大小写锁定
kb.release(keyboard.Key.caps_lock)
上面就是我们 press 和 release 的用法了。
2.2 按下两个按钮
我们可以通过多次调用 press 的方法按下几个按钮,当然我们还有一种简便写法:
from pynput import keyboard
# 创建一个键盘
kb = keyboard.Controller()
# 按下 shift+a
with kb.pressed(keyboard.Key.shift):
kb.press('a')
kb.release('a')
上面的效果就是我们打出了一个 A。
2.3 打字
理论上来说,press 和 release 方法可以完成键盘大多数操作,打字也不例外,但是出于效率的考虑我们可以使用 type 方法:
from pynput import keyboard
# 创建键盘
kb = keyboard.Controller()
# 打字
kb.type('Hello world')
在我们打中文字的时候,输入法并不会影响我们的操作。当时当我们打英文时,如果输入法是中文模式,则会是我们平时打拼音的效果。
2.4 事件监听
键盘的监听同样是由 keyboard 中 Listener 类实现的:
from pynput import keyboard
# 按下按钮
def on_press(key):
print('按下了{0}'.format(key))
# 松开按钮
def on_release(key):
print('松开了{0}'.format(key))
# 监听
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
listener.join()
监听步骤同鼠标一样,这里就不再赘述了。
3. 使用 pynput 完成游戏辅助器
pynput 本身并不具备什么高难度的操作,我们更多的时用它实现一些重复循环的动作,而在玩游戏时,我们刷副本就是一个大量重复的动作。这时候,我们就可以用程序替代我们工作。
我们这里以 PokeMMO 为例,我们先看看游戏本身的键盘分布:
上面这些键就是我们要操作的一些键。下面我们需要实现自动刷怪的功能。如果对这个游戏不了解的话也不要紧,这里只是将其作为一个例子,重要的是将问题转换成代码的步骤。
3.1 刷怪流程
我们首先要知道整个流程是什么,也就是从第一次开始,到第二次重复这段流程的操作。如果对照 PokeMMO 的话,流程如下:
- 第一次的时候自动调整位置,让人物飞到医院
- 从医院出发,到野区
- 使用技能引出野怪(该技能可以使用四次),并战斗
- 循环使用技能引出野怪(四次)
- 回医院补充技能值
- 在医院门口调整位置
上面就是一次完整的流程,我们将每个流程拆分开慢慢实现。
3.2 将每一个步骤都划成一个流程
除了上面的整体流程外,我们每一个步骤都可以划分为一个具体的流程,也就是一系列操作。比如我们的第一个步骤,我们做如下操作:
- 点击宠物
- 选择飞空术
- 选择城市
这样就把我们的第一个步骤划成了一个流程,我们将每个步骤都封装成一个函数,从小到大,实现完整的流程。
3.3 调整位置、让人物飞医院
我们对照键盘,将这一流程转化为代码:
import time
from pynput import keyboard
# 创建一个键盘
kb = keyboard.Controller()
def fly():
"""定义方法,让人物回到医院"""
# 选择宠物
kb.type('3')
time.sleep(0.5)
# 选择飞空术
kb.type('s')
time.sleep(0.5)
# 选择城市,因为飞空术花的时间比较长,这里休眠 7 秒
kb.type('l')
kb.type('l')
time.sleep(7)
这样我们就实现了第一个步骤。
3.4 从医院出发,到野区
这个步骤无非就是上下左右键的改变,根据不同路线每个按键按的时长不一样,我们可以将路线封装成一个列表:
# 0,1,2,3 分别表示上右下左
Lmz_weed = [
# 向左移动 6 秒
(3, 6),
# 向上移动 0.4 秒
(0, 0.4)
]
该列表的元素为元组,每个元组代表一条路线。而元组中的内容则是方向和按下的时长。于是我们通过循环的方式,行走任何路线:
def to_weed(road):
"""根据路线移动"""
# 使用自行车
m_keyboard.type('c')
# 根据线路移动
for i in road:
key = None
if i[0] == 0:
# 向上移动
key = Key.up
elif i[0] == 1:
# 向右移动
key = Key.right
elif i[0] == 2:
# 向下移动
key = Key.down
elif i[0]== 3:
# 向左移动
key = Key.left
# 按下相应方向
m_keyboard.press(key)
time.sleep(i[1])
m_keyboard.release(key)
上面就是我们行走的函数。
3.5 使用技能引出野怪
该步骤可以分为下列流程:
- 选择宠物
- 使用技能
- 攻击宠物
- 循环四次
我们将引出宠物和攻击宠物分别封装成两个函数:
def earthquake():
"""使用地震击败怪物"""
# 使用地震
m_keyboard.type('f')
time.sleep(0.5)
m_keyboard.type('q')
time.sleep(0.5)
m_keyboard.type('e')
time.sleep(37)
def sweet():
"""使用香气甜甜"""
# 选择第二个宠物使用香甜气息
m_keyboard.type('2')
time.sleep(0.5)
m_keyboard.type('s')
time.sleep(15)
写出了上面两个函数,我们只需要用一个简单的循环就可以完成整个流程:
for i in range(4):
# 引出宠物
sweet()
# 攻击
earthquake()
3.6 回医院补充技能值
这需要我们先回到医院,然后进入医院,然后同护士对话:
def to_hospital():
"""去医院"""
# 选择第三个宠物飞空
fly()
# 走进医院
m_keyboard.press(Key.up)
time.sleep(7)
m_keyboard.release(Key.up)
# 对话
for i in range(8):
time.sleep(1.2)
m_keyboard.type('a')
time.sleep(1)
for i in range(3):
time.sleep(1)
m_keyboard.type('b')
# 走出医院
m_keyboard.press(Key.down)
time.sleep(7)
m_keyboard.release(Key.down)
# 对准宠物位置
fly()
在对话的时候,我们模拟日常操作,不断按 a,然后不断按 b。
3.7 完整流程
完整流程就是我们用我们的每个步骤依次调用相应的方法,然后整合在一起:
time.sleep(3)
flag = True
while(True):
"""循环刷野怪"""
if flag:
to_hospital()
flag = False
to_weed(Lmz_weed)
for i in range(4):
sweet()
earthquake()
to_hospital()
看到这里会有许多困惑的地方,主要原因是对游戏不熟悉。不过这个程序本身不是重点,重点是将整个操作化为一系列流程,然后将某一流程切割成一系列操作,最后通过代码实现这些操作。
4. 总结
哪些情况我们可以用 pynput 帮助我们呢?大量重复的操作,像是消息轰炸就是比较典型的一种操作。另外一些我们人类无法企及的手速,这时候我们就可以用电脑来实现。另外大家可以自己发掘 pynput 的作用。