PYWINAUTO入门指南

windows上支持的辅助技术列表

  • Win32 API(backend = "win32")(现在默认)
    • MFC VB6 VCL
  • MS UI Automation(backend = "uia")
    • WinForms, WPF, Store app, QT, Browsers

GUI观察

  • spy++

  • Inspect.exe

    路径在C:\Program Files (x86)\Windows Kits\<winver>\bin\x64

    如果GUI不能满足你的要求,那使用鼠标和键盘模块也是另一种选择,使用pyautogui吧,它可以满足你任何要求。

自动化入口

pywinauto中启动程序使用一个Application对象来调用它。

  1. from pywinauto.application import Application
  2. # 对于Windows中自带应用程序,直接执行,对于外部应用应输入完整路径
  3. app = Application(backend="uia").start('notepad.exe')
  4. #描述Notepad.exe进程中的窗口
  5. dlg_spec = app.UntitledNotepad
  6. #等待窗口真正打开
  7. actionable_dlg = dlg_spec.wait('visible')

如果你想跨进程进到另一个程序中,你可以使用Desktop对象

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

应用程序和桌面对象都是backend特定的,因此无需再使用后台名称了。

窗口规格

这高级pywinauto GUI的核心概念,你可以使用它来模数窗口或者控件的更多细节,无论它是否存在或者已经关闭。窗口规范还保留着有关匹配算法、搜索算法,这些信息将用于获取真实的窗口或者控件。

我们开始创建一个窗口规范:

  1. >>> dlg_spec = app.window(title='Untitled - Notepad')
  2. # 在中文环境下
  3. # dlg_spec = app.window(title='无标题 - 记事本')
  4. >>> dlg_spec
  5. <pywinauto.application.WindowSpecification object at 0x0568B790>
  6. >>> dlg_spec.wrapper_object()
  7. <pywinauto.controls.win32_controls.DialogWrapper object at 0x05639B70>
  8. # 中文 <uiawrapper.UIAWrapper - '无标题 - 记事本', Dialog, 3713039978638135481>

通过warpper_object()方法可以执行实际窗口的查找,它返回真实的窗口或控件的包装器ElementNotFoundError,这个包装器可以通过发送动作或者检索数据来处理窗口控件。

python可以隐藏wrapper_object()调用,简化代码,例如:

  1. dlg_spec.wrapper_object().minimize() # while debugging
  2. dlg_spec.minimize() # in production
  3. # 两行代码完全等价

创建窗口还有更多的标准,下面是几个示例:

  1. # can be multi-level
  2. app.window(title_re='.* - Notepad$').window(class_name='Edit')
  3. # can combine criteria
  4. dlg = Desktop(backend="uia").Calculator
  5. dlg.window(auto_id='num8Button', control_type='Button')

魔法解析属性

Python通过动态解析对象属性来简化窗口规范,但是属性名和变量名一样有相同规范:无空格,逗号和其他特殊符号。幸运的是,pywinauto使用”最佳匹配“算法来查看抵消错别字和小的变化。

  1. app.UntitledNotepad
  2. # is equivalent to
  3. app.window(best_match='UntitledNotepad')

Unicode字符和特殊符号的使用可以通过字典中的项目进行访问(尤其对于中文来说,找不到相应的控件就对其进行字典访问)

  1. app['Untitled - Notepad']
  2. # is the same as
  3. app.window(best_match='Untitled - Notepad')

如何得到魔法属性名称

有几个原则,如何将“最佳匹配“到的金光闪闪的名称附加到控件上。所以如果窗口规范接近于其中一个名称,那你就能成功匹配。

  1. 根据标题(窗口文字,名称):app.Properties.OK.click()
  2. 按标题和控制类型:app.Properties.OKButton.click()
  3. 通过控制类型和数量:app.Properties.Button3.click()(Button和Button1匹配相同按钮,button2匹配下一个按钮)
  4. 通过左上角的标签和控件类型:app.OpenDialog.FileNameEdit.set_text("")
  5. 按控件类型和项目文本:app.Properties.TabControlSharing.select("General")

通常这些所有匹配的名称并非都可以同时使用。要检查指定对话框的这些名称,可以使用print_control_identifiers()方法。可用的最佳匹配名显示为树中每个控件的Python列表。更详细的窗口规范也可以从方法输出中复制。

例如:

app.Properties.child_window(title="Contains:", auto_id="13087", control_type="Edit")

  1. from pywinauto.application import Application
  2. app = Application(backend="uia").start('notepad.exe')
  3. # 查到这个记事本的控件树
  4. dlg_spec = app['无标题 - 记事本']
  5. dlg_spec.print_control_identifiers()
  6. Dialog - '无标题 - 记事本' (L403, T241, R1203, B609)
  7. ['Dialog', '无标题 - 记事本Dialog', '无标题 - 记事本']
  8. child_window(title="无标题 - 记事本", control_type="Window")
  9. |
  10. | Edit - '文本编辑器' (L411, T292, R1195, B601)
  11. | ['', 'Edit', '0', '1']
  12. | child_window(title="文本编辑器", auto_id="15", control_type="Edit")
  13. | |
  14. | | ScrollBar - '垂直滚动条' (L1178, T292, R1195, B601)
  15. | | ['垂直滚动条', '垂直滚动条ScrollBar', 'ScrollBar']
  16. | | child_window(title="垂直滚动条", auto_id="NonClientVerticalScrollBar", control_type="ScrollBar")
  17. | | |
  18. | | | Button - '上一行' (L1178, T292, R1195, B309)
  19. | | | ['Button', '上一行', '上一行Button', 'Button0', 'Button1']
  20. | | | child_window(title="上一行", auto_id="UpButton", control_type="Button")
  21. | | |
  22. | | | Button - '下一行' (L1178, T584, R1195, B601)
  23. | | | ['Button2', '下一行Button', '下一行']
  24. | | | child_window(title="下一行", auto_id="DownButton", control_type="Button")
  25. |
  26. | TitleBar - 'None' (L427, T244, R1195, B272)
  27. | ['2', 'TitleBar']
  28. | |
  29. | | Menu - '系统' (L411, T249, R433, B271)
  30. | | ['Menu', '系统', '系统Menu', '系统0', '系统1', 'Menu0', 'Menu1']
  31. | | child_window(title="系统", auto_id="MenuBar", control_type="MenuBar")
  32. | | |
  33. | | | MenuItem - '系统' (L411, T249, R433, B271)
  34. | | | ['系统2', '系统MenuItem', 'MenuItem', 'MenuItem0', 'MenuItem1']
  35. | | | child_window(title="系统", control_type="MenuItem")
  36. | |
  37. | | Button - '最小化' (L1056, T242, R1103, B272)
  38. | | ['Button3', '最小化', '最小化Button']
  39. | | child_window(title="最小化", control_type="Button")
  40. | |
  41. | | Button - '最大化' (L1103, T242, R1149, B272)
  42. | | ['Button4', '最大化Button', '最大化']
  43. | | child_window(title="最大化", control_type="Button")
  44. | |
  45. | | Button - '关闭' (L1149, T242, R1196, B272)
  46. | | ['Button5', '关闭Button', '关闭']
  47. | | child_window(title="关闭", control_type="Button")
  48. |
  49. | Menu - '应用程序' (L411, T272, R1195, B291)
  50. | ['应用程序', 'Menu2', '应用程序Menu']
  51. | child_window(title="应用程序", auto_id="MenuBar", control_type="MenuBar")
  52. | |
  53. | | MenuItem - '文件(F)' (L411, T272, R463, B291)
  54. | | ['文件(F)MenuItem', '文件(F)', 'MenuItem2']
  55. | | child_window(title="文件(F)", control_type="MenuItem")
  56. | |
  57. | | MenuItem - '编辑(E)' (L463, T272, R516, B291)
  58. | | ['编辑(E)MenuItem', '编辑(E)', 'MenuItem3']
  59. | | child_window(title="编辑(E)", control_type="MenuItem")
  60. | |
  61. | | MenuItem - '格式(O)' (L516, T272, R572, B291)
  62. | | ['格式(O)MenuItem', '格式(O)', 'MenuItem4']
  63. | | child_window(title="格式(O)", control_type="MenuItem")
  64. | |
  65. | | MenuItem - '查看(V)' (L572, T272, R626, B291)
  66. | | ['查看(V)', '查看(V)MenuItem', 'MenuItem5']
  67. | | child_window(title="查看(V)", control_type="MenuItem")
  68. | |
  69. | | MenuItem - '帮助(H)' (L626, T272, R681, B291)
  70. | | ['帮助(H)', '帮助(H)MenuItem', 'MenuItem6']
  71. | | child_window(title="帮助(H)", control_type="MenuItem")
  72. Process finished with exit code 0

一些例子

下面的这些例子所包括:注意:示例是依赖于语言的,他们仅适用于所coding的产品语言,如下所示例的均为英文环境。

  • mspaint.py 控制MSPaint
  • notepad_fast.py 使用快速时间设置来控制笔记本
  • notepad_slow.py 使用慢时间设置来控制笔记本
  • notepad_item.py 使用项目,然后属性访问控件记事本。
  • misc_examples.py 显示一些异常以及如何获取控制标识符。
  • save_from_internet_explorer.py 从Internet Explorer保存网页。
  • save_from_firefox.py 从Firefox保存网页。
  • get_winrar_info.py 如何做多语言自动化的例子。这不是一个理想的例子(适用于法语,捷克语和德语WinRar)
  • forte_agent_sample.py 处理复杂的应用程序的例子是非常动态的,并且在启动时经常给出不同的对话框。
  • windowmediaplayer.py 另一个例子 - 处理ListView中的复选框。
  • test_sakura.pytest_sakura2.py 自动化一个Japanase产品的两个例子。

在命令行自动化记事本

请按照如下示例运行

  1. from pywinauto.application import Application
  2. import time
  3. app = Application().start('notepad.exe')
  4. time.sleep(1)
  5. app[' 无标题 - 记事本 '].menu_select("编辑(&E) -> 替换(&R)..")
  6. time.sleep(1)
  7. app['替换'].取消.click()
  8. # 没有with_spaces 参数空格将不会被键入。请参阅SendKeys的这个方法的文档,因为它是SendKeys周围的薄包装。
  9. app[' 无标题 - 记事本 '].Edit.type_keys("Hi from Python interactive prompt %s" % str(dir()), with_spaces = True)
  10. app[' 无标题 - 记事本 '].menu_select('文件(&F) -> 退出(&X)')
  11. # 在这时候不清楚“不保存”的按钮名就对app['记事本'] 使用print_control_identifiers()
  12. app['记事本'].Button2.click()