没有错,我胡汉三又肥来啦!今天学习的是我认为自动化里最有用的一个部分,用GUI自动化控制键盘。这能帮助我们搞定很多纯粹机械的工作。比如不停的复制、粘贴。

不过这一章也非常长,估计要分上下两篇内容才能学完。

那么,我们开始吧。

20.1 安装pyautogui模块

pyautogui模块可以向Windows系统、MACOS和Linux操作系统发送虚拟按键和鼠标单机事件。Windows操作系统和macOS用户可以简单地使用pip来安装PyAutoGUI。Linux用户需要安装一些pyantogui一来的软件。

安装命令:pip install –user pyautogui

20.2 在macOS上设置无障碍应用程序

作为一项安全措施,macOS通常不会让程序控制鼠标或键盘。要使pyautogui在macOS上工作,你必须将运行Python脚本的程序设置为无障碍应用程序。没有这一步,你的pyautogui函数调用将没有任何效果。

无论你是在Mu、IDLE还是命令行窗口运行Python程序都需要打开该程序。然后打开System Preferences 并进入Accessibility 标签页。当前打开的应用程序将出现在Allow the apps below to control your computer 标签下。勾选Mu、IDLE、Terminal,或任何你用来运行Python脚本的应用程序。系统会体会输入口令,确认这些更改。

20.3 走对路

在开始GUI自动化之前,你需要知道如何避免可能发生的问题。Python能以想象不到的速度移动鼠标和按键。实际上,它可能太快,导致其他程序跟不上。而且,如果出了问题,但你的程序继续到处移动鼠标,可能很难搞清楚程序到底在做什么,或者如何从问题中回复。你的程序可能失去控制,即使它完美地执行了你的指令。如果程序自己在移动鼠标,停止它可能很难,你不能单机IDLE窗口来关闭它。好在有几种方式来避免GUI自动化问题或使其从问题中回复。

20.3.1 暂停和自动防故障装置

如果你的程序出现错误,无法使用键盘和鼠标关闭程序,你可以使用Pyautogui的故障安全功能,快速地将鼠标指针滑动到屏幕的四个角之一。每个pyautogui函数调用在执行动作后都会有1/10秒的延迟,以便让你有足够的时间将鼠标指针移动到一个角落。如果pyautogui随后发现鼠标指针在角落里,会引发pyautogui.FailSafeException异常。非pyautogui指令不会有这个1/10的延迟。

如果你发现自己需要停止pyautogui程序,只需将鼠标指针移向角落即可。

20.3.2 通过注销关闭所有程序

停止失去控制的GUI自动化程序,最简单的方式可能是使用注销功能,这将关闭所有运行的程序。在Windows 和Linux操作系统上,注销的快捷键是ctrl-all-del。在macOS上,注销的快捷键是command-shift-option-Q。注销后你会丢失所有未保存的工作,但至少不需要等计算器完全重启。

20.4 控制鼠标指针

在本节中,你将学习如何利用pyautogui移动鼠标指针,并追踪它在屏幕上的位置,但首先需要理解pyautogui如何处理坐标。

pyautogui的鼠标函数使用x,y坐标。“原点”的x,y都是0,在屏幕的左上角。向右x坐标值增加,向下y坐标值增加。所有坐标都是正整数,没有附属坐标。

“分辨率”是指屏幕的宽度和高度的像素值。如果屏幕的分辨率设置为1920像素 X 1080像素,那么左上角的坐标是(0,0),右下角的坐标是(1919,1079)

pyautogui.size()函数返回两个整数的元组,包含屏幕的宽度和高度的像素数。在交互式环境中输入以下内容:
In [1]: import pyautogui

In [2]: wh = pyautogui.size()

In [3]: wh
Out[3]: Size(width=1792, height=1120)

In [4]: wh[0]
Out[4]: 1792

In [5]: wh.width
Out[5]: 1792

在分辨率为1792 X 1120像素的计算机上,pyautogui.size()返回(1792,1120)。根据屏幕分辨率的不同,返回值可能不一样。size()返回的size对象是一个命名的元组。

“命名的元组”有数字索引,就像普通的元组一样,属性名也像对象一样:wh[0]和wh.width都是以屏幕的宽度为值。

20.4.1 移动鼠标指针

既然理解了屏幕坐标,下面就让我们来移动鼠标指针。pyautogui.moveTo()函数将鼠标指针立即移动到屏幕的指定位置。表示x,y坐标的整数值分别构成了函数的第一个和第二个参数。可选的duration整数或浮点数关键字参数指定了将鼠标指针移到目的位置所需的描述;如果不指定,默认值是0,表示立即移动(在pyautogui函数中,所有的duration关键字参数都是可选的)。在交互式环境中输入以下内容:

In [6]: import pyautogui

In [7]: for i in range(10):
   ...:     pyautogui.moveTo(100,100,duration=0.25)
   ...:     pyautogui.moveTo(200,100,duration=0.25)
   ...:     pyautogui.moveTo(200,200,duration=0.25)
   ...:     pyautogui.moveTo(100,200,duration=0.25)

这个例子根据提供的坐标,以正方形的模式顺时针移动鼠标指针,移动了10次。每次移动耗时0.25秒,因为有关键字参数指定duration=0.25。如果没有指定函数调用的第三个参数,鼠标指针都会马上从一个点移到另一个点。

pyautogui.move()函数“相对于当前的位置”移动鼠标指针。下面的例子同样以正方形的模式移动鼠标指针,只是它从代码开始运行时鼠标指针所在的位置开始,按正方形移动。

In [13]: for i in range(10):
    ...:     pyautogui.move(100,0,duration=0.25)
    ...:     pyautogui.move(0,100,duration=0.25)
    ...:     pyautogui.move(-100,0,duration=0.25)
    ...:     pyautogui.move(0,-100,duration=0.25)

pyautogui.move()也接收3个参数:向右水平移动多少个像素,向下垂直移动多少个像素,以及(可选的)花多少时间完成移动。为第一个或第二个参数提供负整数,鼠标指针将向左或向上移动。

20.4.2 获取鼠标指针位置

调用pyautogui.position()函数,可以确定鼠标指针当前的位置。它将返回函数调用时,鼠标指针x,y坐标的元组。在交互式环境中输入以下内容,每次调用后请移动鼠标指针:

In [14]: pyautogui.position()
Out[14]: Point(x=3271, y=1216)

In [15]: pyautogui.position()
Out[15]: Point(x=2938, y=952)

In [16]: p = pyautogui.position()

In [17]: p
Out[17]: Point(x=375, y=646)

In [18]: p[0]
Out[18]: 375

In [19]: p.x
Out[19]: 375

当然,返回值取决于鼠标指针的位置。

20.5 控制鼠标交互

既然你知道了如何移动鼠标指针,弄清楚了它在屏幕上的位置,就可以开始单击、拖动和滚动鼠标了。

20.5.1 单击鼠标

要向计算机发送虚拟的鼠标单击事件,就调用pyautogui.click()函数。默认情况下,单机将使用鼠标左键,单机发生在鼠标指针当前所在位置。如果希望单机在鼠标指针当前位置意外的地方,可以传入x,y坐标作为可选的第一个和第二个参数。

如果想指定鼠标按键,就加入button关键字参数,值分别为’left’、’middel’或’right’。例如,pyautogui.click(100,150,button=’left’)将在坐标(100,150)处单机鼠标左键。而pyautogui.click(200,250,button=’right’)将在坐标(200,250)处单机鼠标右键。

在交互式环境中输入以下内容:

>>> import pyautogui

>>> pyautogui.click(10,5)

你应该看到鼠标指针移到屏幕左上角的位置并单击。完整的“单击”是指按鼠标按键,然后放开,同时不移动位置。实现单机也可以调用pyautogui.mouseDown(),这只是按下鼠标按键;再调用pyautogui.mouseUp(),释放鼠标按键。这些函数的参数与click()相同。实际上,click()函数只是这两个函数调用的方便封装。

为了更方便,pyautogui.doubleClick()函数只执行双击鼠标左键事件。pyautogui.rightClick()和pyautohui.middleClick()函数将分别执行右键和中键单击事件。

20.5.2 拖动鼠标

“拖动”意味着移动鼠标指针,同时按住一个按键不放。例如,可以通过拖动文件图标,在文件夹之间移动文件,或在日历应用中移动预约。

pyautogui 提供了pyautogui.aragTo()和pyautogui.drag()函数,将鼠标指针拖动到一个新的位置。dragTo()和drag()的参数与moveTo和move()相同;x坐标/水平移动,y坐标/垂直移动,以及可选的时间间隔(在macOS上,如果鼠标移动太快,拖动会不对,所以见识提供duration关键字参数)。

要尝试使用这些函数,请打开一个绘图应用。我将使用pyautogui在这些应用中绘图。

让鼠标指针停留在绘图应用的画布上,同时选中铅笔或画笔工具,在新的文件编辑器窗口输入以下内容,保存为ds.py。

import pyautogui,time
time.sleep(5)
pyautogui.click()
distance = 300
change = 20
while distance > 0:
    pyautogui.drag(distance,0,duration=0.2)
    distance = distance - change
    pyautogui.drag(0,distance,duration=0.2)
    pyautogui.drag(-distance,0, duration=0.2)
    distance = distance - change
    pyautogui.drag(0,-distance, duration=0.2)

这是书上的代码,如果按照这个代码运行会报错:AssertionError: button argument not in (‘left’, ‘middle’, ‘right’)

需要补充参数,修改后代码如下:

import pyautogui,time
time.sleep(5)
pyautogui.click()
distance = 300
change = 20

while distance > 0:
    pyautogui.drag(distance,0,duration=3,button='left')
    distance = distance - change
    pyautogui.drag(0,distance,duration=3,button='left')
    pyautogui.drag(-distance, 0, duration=3,button='left')
    distance = distance - change
    pyautogui.drag(0, -distance, duration=3,button='left')

实际效果图如下:

这里需要说明一下为什么我将速率调为3秒,因为我用的ps画笔工具,如果按书上的0.2秒画笔速度跟不上鼠标速度。就会出现很大的空隙,事实上即使是3秒,也会看到空隙。不过0.2秒的话会更大。

所以建议如果你使用pyautogui鼠标自动化的话,一定要调整鼠标指针速度,使得其更适合你的项目。

在运行这个程序时,会有5秒的延迟,让你选中铅笔或画笔工具,并让鼠标指针停留在画图工具的窗口上。然后sd.py将控制鼠标,单机画图程序获得焦点。如果窗口有闪烁的鼠标指针,它就获得了“焦点”,这是你的动作(例如打字或这个例子中的拖动鼠标)就会影响该窗口。画图程序获取焦点后,sd.py将绘制一个正方形旋转图像。

distance 变量从300开始,所以在while循环的第一次迭代中,第一次drag()调用将鼠标指针向右拖动300像素,花了0.2秒。然后distance降到280,第二次drag()调用将鼠标指针向下拖动280像素。第三次drag()调用将鼠标指针水平拖动-280(向左280)。distabce降到260,最后一次drag()调用将鼠标指针向上拖动260.每次迭代,鼠标指针都向右、向下、向上拖动,distance都比前一次迭代小一点。通过这段代码循环,就可以移动鼠标指针,画出正方形旋转图案。

可以手动画出这个选我,但一定要画的很慢才能这么精确。而pyautogui只需几秒就能画完!

20.5.3 滚动鼠标

最后一个pyautogui鼠标函数是scroll()。你向它提供一个整型参数,说明向上或向下滚动多少单位。单位的意义在每个操作系统和应用上不一样,所以你必须试验,看看在你当前的情况能滚动多远。

滚动发横在鼠标的当前位置。传递正整数表示向上滚动,传递负整数表示向下滚动。将鼠标指针停留在Mu编辑器窗口上,在Mu表及其的交互式环境中运行以下代码:

>>> pyautogui.scroll(200)

如果鼠标指针在可以向上滚动的文本字段上,你会看到Mu向上移动。

这个部分我不知道为什么在macos一直不成功,要说也没什么代码,也没出错。但就是不起效果。不知道是哪里处问题了。但是在liunx上没有问题。

20.6 规划鼠标运动

编写一个能自动单机屏幕的程序的难点之一,就是找到你想单机的物品的x坐标和y坐标。pyautogui.mouseInfo(函数)可以帮助你解决这个问题。

pyautogui.mouseInfo()函数需要在交互式环境中调用,而不是作为程序的一部分。它启动了一个名叫MouseInfo的小应用程序,该应用程序是pyautogui的一部分。这个应用程序的窗口看起来如下图所示:

在交互式环境中输入以下代码:

>>> import pyautogui

>>> pyautogui.mouseInfo()

虽然不是中文……但好在也不复杂,应该都能看懂。

这导致MouseInfo窗口出现。该窗口提供了关于鼠标指针当前位置的信息,以及鼠标指针处的像素的颜色,以3个整数的RGB元组和十六进制值的形式出现。颜色本身会出现在窗口的颜色框中。

为了帮助记录这些坐标或像素信息,你可以单机8个复制或日志记录按钮中的一个。Copy All、Copy XY、CopyRGB和Copy RGB Hex按钮将对应的信息复制到剪贴板上。log ALL、log XY、log RGB和log RGB Hex按钮将对应的信息写入窗口中的大文本字段。你可以通过单机SaveLog按钮,保存这个额文本字段中的文本。

默认情况下,3秒按钮延迟复选框被勾选,在单机复制或日志访谈录按钮与复制日志记录之间,会有3秒的延迟。这让你有很短的时间单机按钮,然后将鼠标指针移动到所需的位置。

取消此复选框,将鼠标指针移动到指定位置,然后按F1到F8复制或使用日志记录鼠标指针位置,这样可能更容易。你可以查看MouseInfo窗口顶部的复制和日志记录菜单,了解哪些键映射到哪些按钮。

例如,取消勾选3秒按钮延迟复选框,然后按F6键的同时在屏幕上移动鼠标指针,并注意鼠标指针是如何在屏幕上移动的。鼠标指针的X和Y坐标被记录在窗口中间的大文本字段中,你可以在以后的Pyautogui脚本中使用这些坐标。

其他都好说,但是color的颜色字段无论怎么弄都是黑色的,且显示NA_on_macOS。我太难了……

20.7 处理屏幕

你的GUI自动化程序没有必要盲目地单机和输入。pyautogui拥有屏幕快照的功能,可以根据当前屏幕的内容创建图形文件。这些函数也可以返回一个pillow的image对象,包含当前屏幕的内容。

20.7.1 获取屏幕快照

要在Python中获取屏幕快照,就调用pyautogui.screenshot()函数。在交互式环境中输入以下内容:

>>> import pyautogui

>>> im = pyautogui.screenshot()

im变量将包含一个屏幕快照的image对象。现在可以调用im变量中的image对象的方法,就像所有其他image对象一样。

20.7.2 分析屏幕快照

假设你的GUI自动化程序中,有一布是单机灰色按钮。在调用click()方法之前,你可以获取屏幕快照,查看脚本要单击处的像素。如果它的颜色和灰色按钮不一样,那么程序就知道出问题了。也许窗口发生了意外的移动,或者弹出式对话框挡住了该按钮。这时,不应该继续,程序可以“看到”它没有单击在正确的信息上,并自行停止。

你可以通过pixel()函数获得屏幕上某一像素点的RGB颜色值。在交互式环境中输入以下内容:

In [3]: import pyautogui

In [4]: pyautogui.pixel((0,0))

以上为书中的代码,如果原样输入则会报错:TypeError: pixel() missing 1 required positional argument: ‘y’

也就是少了一个参数。

这里我的做法是先取消一个括号,把0,0当做x,y。

In [5]: pyautogui.pixel(0,0)
Out[5]: RGB(red=219, green=221, blue=211)

In [6]: pyautogui.pixel(50,200)
Out[6]: RGB(red=247, green=247, blue=247)

传递给pixel()一个坐标的元组,如(0,0)或(50,200),它将告诉你图像中这些坐标处的像素的颜色。pixel()的返回值是一个由3个整数组成的RGB元组,表示像素中的红、绿、蓝三色。(没有第四个alpha值,因为截图图像是完全不透明的)。

pyautogui的pixelMatchesColor()函数将返回True,如果屏幕上给定的x和y坐标出的像素与给定的颜色想匹配,则返回true。第一个和第二个参数是整数的x和y坐标,第三个参数是屏幕像素必须匹配的TGB颜色的3个整数元组。在交互式环境中输入以下内容:

In [7]: import pyautogui

In [8]: pyautogui.pixel(50,200)
Out[8]: RGB(red=247, green=247, blue=247)

In [9]: pyautogui.pixelMatchesColor(50,200,(247,247,247))
Out[9]: True

In [10]: pyautogui.pixelMatchesColor(50,200,(255,255,255))
Out[10]: False

在用pixel()取得特定坐标处像素颜色的RGB元组之后,将同样的坐标和RGB元组传递给pixelMatchesColor(),这应该返回True。然后改变RGB元组中的值,用同样的坐标再次调用pixelMatchesColor(),这应该返回False。你的GUI自动化程序要调用click()之前,这种方法应该有用。请注意,给定坐标处的颜色应该“完全”匹配。即使只是稍有差异,函数也会返回False。

20.8 图像识别

如果事先不知道Pyautogui应该单机哪里,该怎么办?可以使用图像识别功能,想pyautogui提供死亡单机的图像,让它去弄清楚坐标。

例如,如果你以前获得了屏幕快照,截取了提交按钮的图像,保存为submit.png,那么locateOnScreen()函数将返回图像所在处的坐标。要了解咯catOnScreen()函数的工作方式,请获取屏幕上一小块区域的屏幕快照,保存该图像,并在交互式环境中输入以下内容:

In [36]: import pyautogui

In [37]: b = pyautogui.locateOnScreen('submit.png')

In [38]: b
Out[38]: Box(left=3300, top=284, width=230, height=90)

In [39]: b[0]
Out[39]: 3300

In [40]: b.left
Out[40]: 3300

这里要注意一下,这个识别没有那么精确,比如你在一段文字中截图一部分去做识别,很容易识别不出来。更容易达成的方式是要页面中独一无二的色块,比如页面中的大色块按钮。

Box 对象是一个命名的元组,它由locateOnScreen()函数返回,是屏幕上首次发现该图像时左边的x坐标、顶边的y坐标、宽度以及高度。如果你用自己的屏幕快照在你的计算机上尝试,那么返回值会和这里的显示的不一样。

如果屏幕上找不到该图像,locatOnScreen()函数将返回None。请注意,要成功识别,屏幕上的图像必须与提供的图像完全匹配。即使只差一个像素,locateOnScreen()函数也会引发ImageNotFoundException异常。如果你改变了屏幕分辨率,之前截取的图片可能会与当前屏幕上的图片不一致。你可以在操作系统的显示设置中更改缩放比例。

如果该图像在屏幕上能找到多处,locateOnScreen()函数将返回一个Generator对象。可以将它传递给list(),返回一个4个整数元组的列表。在屏幕上找到图像的每个位置,都会有一个4个整数元组。继续在交互式环境的例子中输入以下内容:

In [48]: list(pyautogui.locateAllOnScreen('submit.png'))
Out[48]: 
[Box(left=2842, top=590, width=68, height=62),
 Box(left=2842, top=974, width=68, height=62),
 Box(left=2842, top=1070, width=68, height=62)]

每个4整数元组代表了屏幕上的一个区域。如果图像只找到一次,那么就使list()和locateAllOnScreen()返回的列表只包含一个元组。

在得到图像所在屏幕区域的4个整数元组后,就可以单击这个区域的中信,将该院组传递给click(),在交互式环境中输入以下内容:
In [62]: pyautogui.click(184,416,112,104)

作为快捷方式,你也可以直接将图像文件名传递给click()函数:
In [61]: pyautogui.click('submit.png')

moveTo()he dragTo()函数也接收图像文件名参数。请记住,如果locateOnScreen()在屏幕上找不到图像,就会引发异常,因此应该在try语句中调用它:
In [2]: try:
   ...:     location = pyautogui.locateOnScreen('submit.png')
   ...: except:
   ...:     print('umage could not be found.')

没有try和except语句,未捕获的异常将导致程序崩溃。由于你无法确定程序总能找到该图像,因此在调用locateOnScreen()时最好使用try和except语句。

事实上,就我的测试而言,发现这个并不是很准。或许是因为我有两个屏幕?或许是因为两个屏幕分辨率都不一样?总而言之,在测试click的时候,发现点击的并不准。

20.9 获取窗口信息

使用图像识别功能在屏幕上找东西是一种很脆弱的方式,只要有一个像素点的颜色不一样,pyautogui.locateOnScreen()就找不到图像。如果你需要找到屏幕上某个特定窗口的位置,使用pyautogui窗口能会更快、更可靠。

截止2021年7月31日,本功能仅适用于Windows操作系统,不适用于macos或linux操作系统(如果你不幸只有macOS的话,建议你安装个虚拟机)。

20.9.1获取活动窗口

屏幕上的活动窗口是当前处于前台并且接收键盘输入的窗口。如果你当前正在使用MU

编辑器编写代码,那么Mu编辑器的窗口为活动窗口。屏幕上的所有窗口中,同时仅有一个处于活动状态。

在交互式环境中,调用pyautogui.getActivewindow()函数以获取window对象。

拥有该window对象后,你可以获取它的所有属性。这些属性描述了它的大小、位置和标题。

left、right、top、bottom:一个整数,表示窗口边的x或y坐标。

topleft、topright、bottomleft、midright:两个整数的命名元组,表示窗口边中间的(x,y)坐标。

width,height:一个整数,表示窗口的一个维度,以像素为单位。

size:两个整数的命名元祖,表示窗口的(宽度,高度)。

area:一个整数,表示窗口的面积,以像素为单位。

center:两个整数的命名元祖,表示窗口的中心(x,y)坐标。

centerx、centery:一个整数,表示窗口中心的x或y坐标。

box:4个整数的命名元组,表示窗口(左侧、顶部、宽度、高度)。

title:窗口顶部标题栏中的文本字符串。

例如,要从窗口对象中获取窗口的位置、大小和标题信息,请在交互式环境中输入以下内容:

In [1]: import pyautogui

In [2]: fw = pyautogui.getActiveWindow()

In [3]: fw
Out[3]: Win32Window(hWnd=984174)

In [4]: str(fw)
Out[4]: '<Win32Window left="-24", top="-24", width="7728", height="4248", title="python工程文件 – sea.py">'
In [5]: fw.title
Out[5]: 'python工程文件 – sea.py'
In [6]: fw.size
Out[6]: Size(width=7728, height=4248)
In [7]: fw.left,fw.top,fw.right,fw.bottom
Out[7]: (-24, -24, 7704, 4224)
In [8]: fw.topleft
Out[8]: Point(x=-24, y=-24)
In [9]: fw.area
Out[9]: 32828544
In [12]: pyautogui.click(fw.left+10,fw.top+20)

现在,你可以用这些属性计算窗口内的准确坐标。如果你知道要单击的按钮总是位于窗口左上角的右侧10像素和向下20像素,并且窗口的左上角位于屏幕坐标(300,500),那么调用pyautogui.click(310,520),将单击该按钮。这样,你就不必依靠速度较慢,可靠性较低的locateOnScrenn()函数来找到按钮。

20.9.2 获取窗口的其他方法

尽管getActiveWindow()对于获取函数调用时处于活动状态的窗口很有用,但你需要使用其他函数才能获取屏幕上其他窗口的window对象。

以下4个函数返回window对象的列表。如果它们找不到任何窗口,就会返回一个空列表。

pyautogui.getAllWindows():返回屏幕上所有课件窗口的window对象列表。

pyautogui.getWindowsAt(x,y):返回所有包含点(x,y)的可见窗口的window对象列表。

pyautogui.getWindowsWithTitle(title):返回所有在标题栏中包含字符串title的可见窗口的window对象的列表。

pyautogui.getActiveWindow():返回当前接收键盘焦点的窗口的window对象。

pyautogui还有pyautogui.getAllTitles()函数,该函数返回所有可见窗口的字符串列表。

20.9.3 操纵窗口

窗口属性不仅可以告诉你窗口的大小和位置,还可以做更多的事情。你也可以设置它们的值,以便调整窗口大小或移动窗口。例如,在交互式环境中输入以下内容:

In [13]: import pyautogui

In [14]: fw = pyautogui.getActiveWindow()

In [15]: fw.width
Out[15]: 3525

In [16]: fw.topleft
Out[16]: Point(x=-15, y=72)

In [17]: fw.width = 1000

In [18]: fw.topleft=(800,400)

首先,我们可以使用window对象的属性来查找有关窗口大小和位置的信息。在Mu编辑器中调用这些函数后,窗口应该变窄并移动。

你还可以发现并更改窗口的最小化、最大化和激活状态。尝试在交互式环境中输入以下内容:

In [19]: import pyautogui
In [20]: fw = pyautogui.getActiveWindow()
In [21]: fw.isMaximized
Out[21]: False
In [22]: fw.isMinimized
Out[22]: False
In [23]: fw.isActive
Out[23]: True
In [24]: fw.maximize()
In [25]: fw.isMaximized
Out[25]: True

In [26]: fw.restore()

In [27]: fw.minimize()

In [28]: import time

In [29]: time.sleep(5);fw.activate()

In [30]: fw.close()

isMaximized、isMinimized和isActive属性包含指示窗口当前是否处于该状态的布尔值。maximize()、restore()、minimize()和activate()方法更改窗口的状态。使用maximum()或minimal()最大化或最小化窗口后,restore()方法会将窗口还原到以前的大小和位置。。

close()方法将关闭一个窗口。注意使用这个方法,因为它可能会绕过要求你在退出应用程序之前保存所作工作的所有消息对话框。

此外,你也可以通过PyGetWindow模块,将这些功能与Pyautogui分开使用。

20.10 控制键盘

pyautogui也有一些函数想计算机发送虚拟按键操作,让你能够填充表格,或在应用中输入文本。

20.10.1 通过键盘发送一个字符串

pyautogui.write()函数向计算机发送虚拟按键操作。这些操作产生什么效果,取决于当前获得焦点的窗口和文本输入框。我们可能需要先向文本框发送一次鼠标单机事件,确保它获得焦点。

举一个简单的例子,让我们用Python自动化在文件编辑器窗口输入“hello,world!”。首先,打开一个新的文本编辑器窗口,将它放在屏幕的左上角,以便pyautogui单机正确的位置,让它获得焦点。然后,在交互式环境中输入以下内容:

In [5]: pyautogui.click(100,200);pyautogui.write(‘hello,world!’)

请注意,在同一行中放两个命令,用分号隔开,这让交互式环境不会在两个指令之间提示输入。这防止了你在click()和write()调用之间,不小心让新的窗口获得焦点,从而让这个例子失败。

Python首先在坐标(100,200)处发出虚拟鼠标单机事件,这将单机文件编辑器窗口,让它获得焦点。write()函数调用将窗口发送文本“hello,world!”,结果如图所示:

默认情况下,write()函数将立即输出完整字符串。但是,你可以传入可选的第二个参数,在每个字符之间添加短时间暂停。例如,pyautogui.write(‘hello,world!’,0.25)将在输出H后等待0.25秒,输出e以后再等待0.25秒,一次类推。这种渐进的打自己效果,对于较慢的应用可能有用,它们处理按键时间的速度不够快,跟不上pyautogui。

对于A或者!这样的字符,pyautogui将自动模拟按住Shift键。

20.10.2 键名

不是所有的键都很容易用单个文本字符来表示。例如,如何把shift键或左箭头键表示为单个字符?在pyautogui中,这些键表示为短的字符串值:‘esc’表示ESC键,‘enter’表示Enter键。

除了单个字符串参数,还可以向write()函数传递这些键字符串的列表。例如,以下的调用表示按a键,然后是b键,然后是左箭头两次,最后是x和y键:

>>> pyautogui.write(‘a’,’b’,’left’,’left’,’x’,’y’)

因为按下左箭头将移动键盘光标,所以这回输出XYab。下表列出了pyautogui的键盘按键字符串及其含义,你可以将它传递给write()函数,模拟任何按键组合。

你也可以查看pyautogui.KEYBOARD_KEYS列表,看看pyautogui接收的所有可能的键字符串。‘shift’字符串指的是左边的shift键,它等价于‘shiftleft’。‘ctrl’、‘alt’和‘win’字符串也一样,它们都是指左边的键。

20.10.3 按下和释放键盘按键

就像mouseDown()和mouseUp()函数一样,pyautogui.keyDown()和pyautogui.keyUp()将像计算机发送虚拟的按键和释放命令。它们将根据参数发送建字符串。方便起见,pyautogui提供了pyautogui.press()函数,它调用这两个函数,模拟完整的按键事件。

运行下面的代码,它将输出美元符号(通过按住shift键并按4得到):

In [14]: pyautogui.click(100,200);pyautogui.write(‘I no ‘);pyautogui.keyDown(‘shift’);pyautogui.press(‘4’);pyautogui.keyUp(‘shift’)

这里我多试了一点东西:

In [46]: pyautogui.click(100,200);pyautogui.write('yo yo yo ,everbody ! ');pyperclip.copy('我想娶新垣结衣,but ');pyautogui.hotkey('command','v');pyautogui.write("I don't have" );pyautogui.keyDown('shift');pyautogui.press('4');pyautogui.keyUp('shift')

这里我的目的是要试试中文,因为pyautogui是不支持中文的,所以想要解决,实际上要使用复制粘贴的方式。

这里要引用pyperclip()模块,然后在pyperclip.copy()里输入中文。然后在通过pyautogui.hotkey组合键输出。

不过我发现一个问题,那就是如果开头是中文,就不会输出中文。前面必须是英文开头,后面才可以正常输入中文。否则则会略过中文。

In [47]: pyautogui.click(100,200);pyperclip.copy('我想娶新垣结衣,but ');pyautogui.hotkey('command','v');pyautogui.write("I don't have" );pyautogui.keyDown('shift');pyautogui.press('4');pyautogui.keyUp('shift')

这行代码按住shift键,按下并释放4键,然后再释放shift键。如果你需要在文本框中输入一个字符串,使用write()函数就更合适。但对于接收单个按键命令的作用,使用press()函数是更简单的方式。

20.10.4 快捷键组合

“快捷键”或“热键”是一种按键组合,它调用某种应用功能。复制选择内容的常用快捷键是Ctrl-C(Windows和linux上是这样)。用户按住ctrl键,然后按C键,然后释放C和ctrl键。如果要用keyDown和KeyUp函数来做就很复杂。

我们可以使用组合键,它接收多个键字符串参数,按顺序按下,再按相反的顺序释放。

>>> pyautogui.hotkey(‘ctrl’,’c’)

这个函数对于较大的快捷组合特别有用。在word中,,ctrl-alt-shift-s快捷键组合可以显示样式窗格。不必进行8次不同的函数调用,你只需要hotkey(‘ctrl’,’alt’,’shift’,’s’)就可以了。

20.11 设置GUI自动化脚本

对于自动化那些繁琐的工作,使用GUI自动化脚本是一个很好的方式,但是你的脚本也可能过分挑剔。如果一个窗口在桌面的位置不对,或者一些弹出式窗口意外地出现,你的脚本可能会在屏幕上单机错误的东西。以下是一些关于设置你的GUI自动化脚本的技巧。

1、每次运行脚本单击的应用程序窗口应该最大化,这样每次运行脚本时,它的按钮和菜单都在同一个地方。

2、每次运行脚本时使用相同的屏幕分辨率,这样窗口的位置就不会改变。

3、在等待内容加载的过程中,要加入足够的暂停时间;你不希望脚本在应用程序准备好之前就开始单击。

4、使用locateOnScreen()来找到要单击的按钮和菜单,而不是依赖x、y坐标。如果你的脚本找不到需要单击的东西,就停止程序,而不是让它继续瞎点。

5、使用getWindowsWithTitle()来确保你认为脚本正单击的应用程序窗口是存在的,并使用activate()方法将该窗口放在前台。

6、使用第11章的日志模块来保存脚本所做事情的日志文件。这样一来,如果你不得不中途停止脚本,你就可以改变它,从它上次结束的地方重新开始。

7、在你的脚本中加入尽可能多的检查。想一想,如果出现一个意外的弹出窗口,或者你的计算机失去网络连接,它可能会失败。

8、你可能需要在脚本刚开始时监督该脚本的执行,确保它正常工作。

你可能还需要在脚本的开始处设置一个暂停,这样脚本可以设置脚本将单击的窗口。pyautogui有一个sleep()函数,它的作用于time.sleep()函数相同。还有一个countdown()函数,它可以输出倒计时的数字,给用一个视觉上的指示,说明脚本即将执行。

在交互式环境中输入以下内容:

In [53]: import pyautogui

In [54]: pyautogui.sleep(3)

In [55]: pyautogui.countdown(10)
10 9 8 7 6 5 4 3 2 1 

In [56]: print('倒计时开始:',end='');pyautogui.countdown(3)
倒计时开始:3 2 1 

这些技巧让你的GUI自动化脚本更容易使用,并能从不可预见的情况中恢复。

今天的课程就是这样啦。

总体来说今天学习的更多都是支零破碎的点,如何把点连成线,最后画成面,还需要大量的实战才能去完成。

本来还以为需要两天,实际上一天就完事了。

那么下次再见吧~

print(‘bye~bye~’)

胭惜雨

2021年07月31日

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据