pywinauto简介

 

一、pywinauto简介

pywinauto官方文档链接如下。

https://pywinauto.readthedocs.io/en/latest/

https://www.kancloud.cn/gnefnuy/pywinauto_doc

https://github.com/pywinauto

  • Dialog 是一个窗口,包含其他几个GUI元素/控件,如按钮,编辑框等。对话框不一定是主窗口。 主窗体顶部的消息框也是一个对话框。 主窗口也被pywinauto视为对话框。
  • control 控件是层次结构的任何级别的GUI元素。 该定义包括窗口,按钮,编辑框,表格,表格单元格,工具栏等。
  • Win32 API技术(pywinauto中的“win32”后端)为每个控件提供标识符。 这是一个名为handle的唯一整数.
  • UI Automation API(pywinauto中的“uia”后端)可能不为每个GUI元素提供窗口handle。 “win32”后端看不到这样的元素。 但是Inspect.exe可以显示属性NativeWindowHandle,如果它可用的话。

 

二、访问技术选择

pywinauto要操作应用,首先需要访问应用,主要有两种访问技术。WIN32访问技术支持MFC、VB6、VCL、简单WinForms控件开发的应用,MS UI Automation访问技术支持WinForms、WPS、QT5、WPF、Store apps、browsers等开发的应用。

分析一个应用软件可以用哪种访问技术,有如下常用工具:

1、SPY++,用于WIN32 API。当SPY++可以显示所有的控件时,访问技术应该选择"win32"

2、Inspect.exe:如果Inspect.exe的模式设置为UIA模式,可以比SPY++显示更多的控件,则访问技术应该选择"uia"

3、py_inspect:支持win32和uia两种访问技术,是SWAPY的替代。

https://github.com/pywinauto/py_inspect

4、UISPY:支持uia访问技术

5、SWAPY:只支持win32访问技术。https://github.com/pywinauto/SWAPY

下图是用py_inspect查看的secureCRT控件,可以看到如果选择uia格式,可以看到更多的控件,所以访问技术应该选择‘uia’

其它常用的

 

三、访问应用方法

你可以打开一个应用,或者连接一个已经存在的应用实例。都是通过Application对象完成的。它不是对subprocess.Popen的克隆,而是应用自动化的接入点。这里主要是限制自动化控制进程的范围。如一个程序有多个实例,自动化控制一个实例,而保证其他实例(进程)不受影响。主要有两种对象可以建立这种入口点Application() ,Desktop()。 Application的作用范围是一个进程,如一般的桌面应用程序都为此类。 Desktop的作用范围可以跨进程。主要用于像win10的计算器这样包含多个进程的程序。这种目前比较少见。

1、Application对象

1.1、打开一个应用

connect(self, **kwargs)  # instance method: 

timeout参数是可选的,只有在应用程序需要很长时间才能启动时才需要使用它。

from pywinauto.application import Application
app = Application(backend="uia").start('notepad.exe')
# describe the window inside Notepad.exe process
dlg_spec = app.UntitledNotepad
# wait till the window is really open
actionable_dlg = dlg_spec.wait('visible')

1.2、连接一个应用

connect(self, **kwargs)  # instance method: 

connect() 在要启动自动化应用程序时使用。 要指定已在运行的应用程序,您需要指定以下之一:

process:应用程序的进程ID,例如app = Application().connect(process=2341)
handle:应用程序窗口的窗口句柄,例如,app = Application().connect(handle=0x010f0c)
path:进程的可执行文件的路径(GetModuleFileNameEx用于查找每个进程的路径并与传入的值进行比较),例如:app = Application().connect(path=r"c:\windows\system32\notepad.exe")

或者指定窗口的参数的任意组合,这些都被传递给pywinauto.findwindows.find_elements() 函数。 例如

app = Application().connect(title_re=".*Notepad", class_name="Notepad")

 

2、Desktop对象

from subprocess import Popen
from pywinauto import Desktop
Popen('calc.exe', shell=True)
dlg = Desktop(backend="uia").Calculator
dlg.wait('visible')

 

四、访问窗口方法

这是高级pywinauto API的核心概念。 您可以近似或更详细地描述任何窗口或控件,即使它尚不存在或已经关闭。 窗口规范还保留有关将用于获得真实窗口或控件的匹配/搜索算法的信息。

一个详细的窗口规范如下:

>>> dlg_spec = app.window(title='Untitled - Notepad')
>>> dlg_spec
<pywinauto.application.WindowSpecification object at 0x0568B790>
>>> dlg_spec.wrapper_object()
<pywinauto.controls.win32_controls.DialogWrapper object at 0x05639B70>

实际窗口查找由wrapper_object()方法执行。 它返回实际现有窗口/控件的一些包装器或引发ElementNotFoundError。 这个包装器可以通过发送动作或检索数据来处理窗口/控件。

但是Python可以隐藏这个wrapper_object()调用,这样你就可以在程序中拥有更紧凑的代码。 以下描述完全相同:

dlg_spec.wrapper_object().minimize() # while debugging
dlg_spec.minimize() # in production

创建窗口规范有许多可能的标准。 下面只是几个例子。可以在pywinauto.findwindows.find_elements()函数中找到可能的标准列表。

# 可以是多层次的
app.window(title_re='.* - Notepad$').window(class_name='Edit')

# 可以结合标准
dlg = Desktop(backend="uia").Calculator
dlg.window(auto_id='num8Button', control_type='Button') 
1.通过窗体名确定窗体

dlg = app.Notepad 
dlg = app['Notepad'] 

2、top_window()

这将返回具有应用程序顶级窗口的最高Z顺序的窗口。

3、通过多层次的描述指定一个窗口,如(a),或使用组合参数指定一个窗体如(b),下面两条语句确定的是同一个窗体

dlg = app.window(title_re="Page Setup").window(class_name="#32770")
dlg = app.window(title_re="Page Setup", class_name="#32770")

4、通过findwindows

pywinauto.findwindows.find_windows()

pywinauto.findwindows.find_elements()返回所有的已运行程序的win32_element_info.HwndElementInfo

5、dialogs = app.windows() 

这将返回应用程序的所有可见,启用的顶级窗口的列表。 然后,您可以使用handleprops模块中的一些方法选择所需的对话框。 一旦掌握了所需的句柄,就可以使用

app.window(handle=win)

注意: 如果对话框的标题很长 - 那么输入的属性访问可能会很长,在这种情况下通常更容易使用

app.window(title_re=".*Part of Title.*")

五、访问控件

 

1、属性解析魔法

Python通过动态解析对象属性简化了创建窗口规范。 但是一个属性名称与任何变量名称具有相同的限制:没有空格,逗号和其他特殊符号。 但幸运的是pywinauto使用“最佳匹配”算法来查找拼写错误和小变化。

app.UntitledNotepad
# 相当于
app.window(best_match='UntitledNotepad') 

通过类似字典的项目访问,可以使用Unicode字符和特殊符号。

app['Untitled - Notepad']
# 是相同的
app.window(best_match='Untitled - Notepad') 

2、如何知道魔法属性名称

如何将“最佳匹配”附加到控件上有几个原则。 如果窗口规范接近其中一个名称,您将获得成功的名称匹配。

  1. 按标题(窗口文字,名称): app.Properties.OK.click()
  2. 按标题和控件类型: app.Properties.OKButton.click()
  3. 按控件类型和编号: app.Properties.Button3.click() (注意: Button0和Button1匹配相同的按钮,Button2是下一个,等等。)
  4. 按左上角标签和控件类型: app.OpenDialog.FileNameEdit.set_text("")
  5. 按控件类型和项目文本:app.Properties.TabControlSharing.select("General")

通常并非所有这些匹配的名称都可以同时使用。 要检查指定对话框的这些名称,可以使用print_control_identifiers()方法。 可能的“best_match”名称显示为树中每个控件的Python列表。 也可以从方法输出中复制更详细的窗口规范。 比方说 app.Properties.child_window(title="Contains:", auto_id="13087", control_type="Edit")

3、如何在对话框上指定控件

有许多方法可以指定控件,最简单的方法是

app.dlg.control
app['dlg']['control'] 

第二个更适合非英语操作系统,你需要传递unicode字符串,例如 app [u'对话框标题'] [u'控件标题']

该代码根据以下内容为每个控件构建多个标识符:

  • title
  • friendly class
  • title + friendly class

如果控件的标题文本为空(删除非char字符后),则不使用此文本。 相反,我们寻找控件上方和右侧最接近的标题文本。 并追加友好类。 所以列表变成了

  • friendly class
  • closest text + friendly class

一旦为对话框中的所有控件创建了一组标识符,我们就消除它们的歧义。

使用 WindowSpecification.print_control_identifiers() 方法

注意 通过此方法打印的标识符已经过使标识符唯一的过程。 因此,如果您有两个编辑框,则它们的标识符中都会列出“Edit”。 实际上,虽然第一个可以被称为“Edit”, “Edit0”, “Edit1”和第二个应该被称为“Edit2”

 

六、操作控件

https://pywinauto.readthedocs.io/en/latest/code/code.html#controls-reference

具体操作方法参考官方介绍,注意两点

1、对于同一个控件,用win32查出来的访问路径和用uia查询出来的访问路径不同

2、对于同一个控件,win32访问时可用的方法和用uia访问时可用的方法不同。

 

七、其他

1、如何将pywinauto与英语以外的应用程序语言一起使用

由于Python不支持代码中的unicode标识符,因此您无法使用属性访问来引用控件,因此您必须使用项访问权或对window()进行显式调用。
所以不要写作

app.dialog_ident.control_ident.click() 

你必须写

app['dialog_ident']['control_ident'].click() 

或者明确地使用window()

app.window(title_re="非Ascii字符").window(title="非Ascii字符").click() 

 

2、如何处理未按预期响应的控件(例如OwnerDraw控件)

某些控件(尤其是Ownerdrawn控件)不会按预期响应事件。 例如,如果您查看任何HLP文件并转到“索引”选项卡(单击“搜索”按钮),您将看到一个列表框。 在此运行Spy或Winspector将向您显示它确实是一个列表框 - 但它是ownerdrawn。 这意味着开发人员告诉Windows他们将覆盖项目的显示方式并自行完成。 在这种情况下,他们已经使它无法检索字符串:-(。

那导致什么问题呢?

app.HelpTopics.ListBox.texts()                # 1
app.HelpTopics.ListBox.select("ItemInList")   # 2 
  1. 将返回一个空字符串列表,这一切都意味着pywinauto无法获取列表框中的字符串
  2. 这将因IndexError而失败,因为ListBox的select(string)方法在文本中查找项目以了解它应该选择的项目的索引。

以下解决方法将适用于此控件

app.HelpTopics.ListBox.select(1) 

这将选择列表框中的第二项,因为它不是字符串查找,它可以正常工作。

不幸的是,即便这样也无济于事。 开发人员可以使控件不响应Select等标准事件。 在这种情况下,您可以在列表框中选择项目的唯一方法是使用TypeKeys()的键盘模拟。

这允许您将任何击键发送到控件。 所以要选择你要使用的第3个项目

app.Helptopics.ListBox1.type_keys("{HOME}{DOWN 2}{ENTER}") 
  • {HOME} 将确保突出显示第一个项目。
  • {DOWN 2} 然后将突出显示两个项目
  • {ENTER} 将选择突出显示的项目

如果您的应用程序广泛使用类似的控件类型,那么您可以通过从ListBox派生新类来更轻松地使用它,这可以使用有关您的特定应用程序的额外知识。 例如,在WinHelp示例中,每次在列表视图中突出显示某个项目时,其文本都会插入到列表上方的Edit控件中,并且您可以从那里获取该项目的文本,例如:

# 打印列表框中当前所选项目的文本
# (只要你没有输入编辑控件!)
print app.HelpTopics.Edit.texts()[1] 

 

 

  • 5
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值