[TOC]
# 入門指南
一旦你安裝了pywinauto - 你怎么樣? 第一個必要的事情是確定哪種可訪問性技術(pywinauto的backend)可以用于您的應用程序。
Windows上受支持的輔助功能技術列表:
> * **Win32 API** (`backend="win32"`) - 現在的默認backend
>
> * MFC, VB6, VCL, 簡單的WinForms控件和大多數舊的遺留應用程序
>
> * **MS UI Automation** (`backend="uia"`)
>
> * WinForms, WPF, Store apps, Qt5, 瀏覽器
>
> 注意: Chrome在啟動之前需要`--force-renderer-accessibility` cmd標志。 由于comtypes Python庫限制,不支持自定義屬性和控件。
>
到目前為止,Linux上的AT SPI和Apple Accessibility API都是長期計劃。
## GUI對象檢查/Spy工具
如果您仍然不確定哪個backend最適合您,請嘗試使用免費提供的對象檢查/Spy工具:從GitHub repo [gui-inspect-tool](https://github.com/blackrosezy/gui-inspect-tool)下載它們.
> * **Spy++** 包含在MS Visual Studio發行版(甚至是Express或Community)中,可通過“開始”菜單訪問。 它使用Win32 API。 這意味著如果Spy ++能夠顯示所有控件,那么“win32”`backend就是你需要的。 *AutoIt Window Info*工具是一種Spy ++克隆。
>
> * **Inspect.exe** 是Microsoft創建的另一個很棒的工具。 它包含在Windows SDK中,因此可以在x64 Windows上的以下位置找到它:
>
> ~~~
> C:\Program Files (x86)\Windows Kits\<winver>\bin\x64
> ~~~
>
> 將Inspect.exe切換到**UIA mode**(使用MS UI Automation)。 如果它可以顯示比Spy ++更多的控件及其屬性,那么可能是 `"uia"`backend是你的選擇。
>
> * [py\_inspect](https://github.com/pywinauto/py_inspect) 是基于pywinauto的多后端間諜工具的原型。 在可用后端之間切換可以通過“win32”和“uia”后端向您顯示層次結構的差異。 **py \ _inspect**是[SWAPY](https://github.com/pywinauto/SWAPY)的未來替代品,僅在pywinauto == 0.5.4出現時支持“win32”后端。 由于現代pywinauto 0.6.0+架構,py \ _insins的初始實現僅包含大約150行代碼。
>
如果所有檢測工具都看不到某些或所有控件,則仍然可以通過使用基本模塊鼠標和鍵盤生成鼠標和鍵盤事件來控制應用程序。
## 自動化的切入點
所以你有一個應用程序,你知道它支持上面提到的一種可訪問性技術。 下一步是什么?
首先,您應該啟動應用程序或連接到現有的應用程序實例。 它可以使用`Application`對象完成。 這不僅僅是`subprocess.Popen`的克隆,而是進一步自動化的入口點,通過進程邊界限制所有范圍。 控制可能很少的應用程序實例很有用(您使用一個不打擾另一個實例的實例)。
> ~~~
> from pywinauto.application import Application
> app = Application(backend="uia").start('notepad.exe')
>
> # 描述Notepad.exe進程內的窗口
> dlg_spec = app.UntitledNotepad
> # 等到窗戶真的開著
> actionable_dlg = dlg_spec.wait('visible')
> ~~~
如果你想要跨越流程邊界(比如Win10計算器令人驚訝地在多個流程中繪制它的小部件),你的入口點是一個`Desktop`對象。
> ~~~
> from subprocess import Popen
> from pywinauto import Desktop
>
> Popen('calc.exe', shell=True)
> dlg = Desktop(backend="uia").Calculator
> dlg.wait('visible')
> ~~~
**Application**和**Desktop**對象都是backend特定的。 無需在后續操作中明確使用backend名稱。
## 窗口規格
這是高級pywinauto API的核心概念。 您可以近似或更詳細地描述任何窗口或控件,即使它尚不存在或已經關閉。 窗口規范還保留有關將用于獲得真實窗口或控件的匹配/搜索算法的信息。
讓我們創建一個詳細的窗口規范:
> ~~~
> >>> dlg_spec = app.window(title=u'無標題 - 記事本')
>
> >>> 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() # 在調試時
> dlg_spec.minimize() # 在生產環境中
> ~~~
創建窗口規范有許多可能的標準。 這只是幾個例子。
> ~~~
> # 可以是多層次的
> app.window(title_re='.* - Notepad$').window(class_name='Edit')
>
> # 可以結合標準
> dlg = Desktop(backend="uia").Calculator
> dlg.window(auto_id='num8Button', control_type='Button')
> ~~~
可以在[`pywinauto.findwindows.find_elements()`](code/pywinauto.findwindows.html#pywinauto.findwindows.find_elements“pywinauto.findwindows.find_elements”)函數中找到可能的標準列表。
## 屬性解析魔法
Python通過動態解析對象屬性簡化了創建窗口規范。 但是一個屬性名稱與任何變量名稱具有相同的限制:沒有空格,逗號和其他特殊符號。 但幸運的是pywinauto使用“最佳匹配”算法來查找拼寫錯誤和小變化。
> ~~~
> app.UntitledNotepad
> # 相當于
> app.window(best_match='UntitledNotepad')
> ~~~
通過類似字典的項目訪問,可以使用Unicode字符和特殊符號。
> ~~~
> app['Untitled - Notepad']
> # 是相同的
> app.window(best_match='Untitled - Notepad')
> ~~~
## 如何知道魔法屬性名稱
如何將“最佳匹配”金牌附加到控件上有幾個原則。 因此,如果窗口規范接近其中一個名稱,您將獲得成功的名稱匹配。
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")`。
> ~~~
> >>> app.Properties.print_control_identifiers()
>
> Control Identifiers:
>
> Dialog - 'Windows NT Properties' (L688, T518, R1065, B1006)
> [u'Windows NT PropertiesDialog', u'Dialog', u'Windows NT Properties']
> child_window(title="Windows NT Properties", control_type="Window")
> |
> | Image - '' (L717, T589, R749, B622)
> | [u'', u'0', u'Image1', u'Image0', 'Image', u'1']
> | child_window(auto_id="13057", control_type="Image")
> |
> | Image - '' (L717, T630, R1035, B632)
> | ['Image2', u'2']
> | child_window(auto_id="13095", control_type="Image")
> |
> | Edit - 'Folder name:' (L790, T596, R1036, B619)
> | [u'3', 'Edit', u'Edit1', u'Edit0']
> | child_window(title="Folder name:", auto_id="13156", control_type="Edit")
> |
> | Static - 'Type:' (L717, T643, R780, B658)
> | [u'Type:Static', u'Static', u'Static1', u'Static0', u'Type:']
> | child_window(title="Type:", auto_id="13080", control_type="Text")
> |
> | Edit - 'Type:' (L790, T643, R1036, B666)
> | [u'4', 'Edit2', u'Type:Edit']
> | child_window(title="Type:", auto_id="13059", control_type="Edit")
> |
> | Static - 'Location:' (L717, T669, R780, B684)
> | [u'Location:Static', u'Location:', u'Static2']
> | child_window(title="Location:", auto_id="13089", control_type="Text")
> |
> | Edit - 'Location:' (L790, T669, R1036, B692)
> | ['Edit3', u'Location:Edit', u'5']
> | child_window(title="Location:", auto_id="13065", control_type="Edit")
> |
> | Static - 'Size:' (L717, T695, R780, B710)
> | [u'Size:Static', u'Size:', u'Static3']
> | child_window(title="Size:", auto_id="13081", control_type="Text")
> |
> | Edit - 'Size:' (L790, T695, R1036, B718)
> | ['Edit4', u'6', u'Size:Edit']
> | child_window(title="Size:", auto_id="13064", control_type="Edit")
> |
> | Static - 'Size on disk:' (L717, T721, R780, B736)
> | [u'Size on disk:', u'Size on disk:Static', u'Static4']
> | child_window(title="Size on disk:", auto_id="13107", control_type="Text")
> |
> | Edit - 'Size on disk:' (L790, T721, R1036, B744)
> | ['Edit5', u'7', u'Size on disk:Edit']
> | child_window(title="Size on disk:", auto_id="13106", control_type="Edit")
> |
> | Static - 'Contains:' (L717, T747, R780, B762)
> | [u'Contains:1', u'Contains:0', u'Contains:Static', u'Static5', u'Contains:']
> | child_window(title="Contains:", auto_id="13088", control_type="Text")
> |
> | Edit - 'Contains:' (L790, T747, R1036, B770)
> | [u'8', 'Edit6', u'Contains:Edit']
> | child_window(title="Contains:", auto_id="13087", control_type="Edit")
> |
> | Image - 'Contains:' (L717, T773, R1035, B775)
> | [u'Contains:Image', 'Image3', u'Contains:2']
> | child_window(title="Contains:", auto_id="13096", control_type="Image")
> |
> | Static - 'Created:' (L717, T786, R780, B801)
> | [u'Created:', u'Created:Static', u'Static6', u'Created:1', u'Created:0']
> | child_window(title="Created:", auto_id="13092", control_type="Text")
> |
> | Edit - 'Created:' (L790, T786, R1036, B809)
> | [u'Created:Edit', 'Edit7', u'9']
> | child_window(title="Created:", auto_id="13072", control_type="Edit")
> |
> | Image - 'Created:' (L717, T812, R1035, B814)
> | [u'Created:Image', 'Image4', u'Created:2']
> | child_window(title="Created:", auto_id="13097", control_type="Image")
> |
> | Static - 'Attributes:' (L717, T825, R780, B840)
> | [u'Attributes:Static', u'Static7', u'Attributes:']
> | child_window(title="Attributes:", auto_id="13091", control_type="Text")
> |
> | CheckBox - 'Read-only (Only applies to files in folder)' (L790, T825, R1035, B841)
> | [u'CheckBox0', u'CheckBox1', 'CheckBox', u'Read-only (Only applies to files in folder)CheckBox', u'Read-only (Only applies to files in folder)']
> | child_window(title="Read-only (Only applies to files in folder)", auto_id="13075", control_type="CheckBox")
> |
> | CheckBox - 'Hidden' (L790, T848, R865, B864)
> | ['CheckBox2', u'HiddenCheckBox', u'Hidden']
> | child_window(title="Hidden", auto_id="13076", control_type="CheckBox")
> |
> | Button - 'Advanced...' (L930, T845, R1035, B868)
> | [u'Advanced...', u'Advanced...Button', 'Button', u'Button1', u'Button0']
> | child_window(title="Advanced...", auto_id="13154", control_type="Button")
> |
> | Button - 'OK' (L814, T968, R889, B991)
> | ['Button2', u'OK', u'OKButton']
> | child_window(title="OK", auto_id="1", control_type="Button")
> |
> | Button - 'Cancel' (L895, T968, R970, B991)
> | ['Button3', u'CancelButton', u'Cancel']
> | child_window(title="Cancel", auto_id="2", control_type="Button")
> |
> | Button - 'Apply' (L976, T968, R1051, B991)
> | ['Button4', u'ApplyButton', u'Apply']
> | child_window(title="Apply", auto_id="12321", control_type="Button")
> |
> | TabControl - '' (L702, T556, R1051, B962)
> | [u'10', u'TabControlSharing', u'TabControlPrevious Versions', u'TabControlSecurity', u'TabControl', u'TabControlCustomize']
> | child_window(auto_id="12320", control_type="Tab")
> | |
> | | TabItem - 'General' (L704, T558, R753, B576)
> | | [u'GeneralTabItem', 'TabItem', u'General', u'TabItem0', u'TabItem1']
> | | child_window(title="General", control_type="TabItem")
> | |
> | | TabItem - 'Sharing' (L753, T558, R801, B576)
> | | [u'Sharing', u'SharingTabItem', 'TabItem2']
> | | child_window(title="Sharing", control_type="TabItem")
> | |
> | | TabItem - 'Security' (L801, T558, R851, B576)
> | | [u'Security', 'TabItem3', u'SecurityTabItem']
> | | child_window(title="Security", control_type="TabItem")
> | |
> | | TabItem - 'Previous Versions' (L851, T558, R947, B576)
> | | [u'Previous VersionsTabItem', u'Previous Versions', 'TabItem4']
> | | child_window(title="Previous Versions", control_type="TabItem")
> | |
> | | TabItem - 'Customize' (L947, T558, R1007, B576)
> | | [u'CustomizeTabItem', 'TabItem5', u'Customize']
> | | child_window(title="Customize", control_type="TabItem")
> |
> | TitleBar - 'None' (L712, T521, R1057, B549)
> | ['TitleBar', u'11']
> | |
> | | Menu - 'System' (L696, T526, R718, B548)
> | | [u'System0', u'System', u'System1', u'Menu', u'SystemMenu']
> | | child_window(title="System", auto_id="MenuBar", control_type="MenuBar")
> | | |
> | | | MenuItem - 'System' (L696, T526, R718, B548)
> | | | [u'System2', u'MenuItem', u'SystemMenuItem']
> | | | child_window(title="System", control_type="MenuItem")
> | |
> | | Button - 'Close' (L1024, T519, R1058, B549)
> | | [u'CloseButton', u'Close', 'Button5']
> | | child_window(title="Close", control_type="Button")
>
> ~~~
## 看看這些例子
包括以下示例: **注意**: 示例取決于語言 - 它們僅適用于為其編程的產品語言。 除非突出顯示,否則所有示例都已編程為英語軟件。
* `mspaint.py` 控制畫圖
* `notepad_fast.py` 使用快速計時設置來控制記事本
* `notepad_slow.py` 使用慢速計時設置來控制記事本
* `notepad_item.py` 使用item而不是屬性訪問來控制記事本。
* `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.py`, `test_sakura2.py` 兩個自動化日本產品的例子。
## 在命令行自動記事本
請在下面找到一個示例運行
~~~
C:\>python
Python 2.4.2 (#67, Sep 28 2005, 12:41:11) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(1) >>> from pywinauto import application
(2) >>> app = application.Application()
(3) >>> app.start("Notepad.exe")
<pywinauto.application.Application object at 0x00AE0990>
(4) >>> app.UntitledNotepad.draw_outline()
(5) >>> app.UntitledNotepad.menu_select("Edit -> Replace")
(6) >>> app.Replace.print_control_identifiers()
Control Identifiers:
Dialog - 'Replace' (L179, T174, R657, B409)
['ReplaceDialog', 'Dialog', 'Replace']
child_window(title="Replace", class_name="#32770")
|
| Static - 'Fi&nd what:' (L196, T230, R292, B246)
| ['Fi&nd what:Static', 'Fi&nd what:', 'Static', 'Static0', 'Static1']
| child_window(title="Fi&nd what:", class_name="Static")
|
| Edit - '' (L296, T226, R524, B250)
| ['Fi&nd what:Edit', 'Edit', 'Edit0', 'Edit1']
| child_window(class_name="Edit")
|
| Static - 'Re&place with:' (L196, T264, R292, B280)
| ['Re&place with:', 'Re&place with:Static', 'Static2']
| child_window(title="Re&place with:", class_name="Static")
|
| Edit - '' (L296, T260, R524, B284)
| ['Edit2', 'Re&place with:Edit']
| child_window(class_name="Edit")
|
| CheckBox - 'Match &whole word only' (L198, T304, R406, B328)
| ['CheckBox', 'Match &whole word onlyCheckBox', 'Match &whole word only', 'CheckBox0', 'CheckBox1']
| child_window(title="Match &whole word only", class_name="Button")
|
| CheckBox - 'Match &case' (L198, T336, R316, B360)
| ['CheckBox2', 'Match &case', 'Match &caseCheckBox']
| child_window(title="Match &case", class_name="Button")
|
| Button - '&Find Next' (L536, T220, R636, B248)
| ['&Find Next', '&Find NextButton', 'Button', 'Button0', 'Button1']
| child_window(title="&Find Next", class_name="Button")
|
| Button - '&Replace' (L536, T254, R636, B282)
| ['&ReplaceButton', '&Replace', 'Button2']
| child_window(title="&Replace", class_name="Button")
|
| Button - 'Replace &All' (L536, T288, R636, B316)
| ['Replace &AllButton', 'Replace &All', 'Button3']
| child_window(title="Replace &All", class_name="Button")
|
| Button - 'Cancel' (L536, T322, R636, B350)
| ['CancelButton', 'Cancel', 'Button4']
| child_window(title="Cancel", class_name="Button")
|
| Button - '&Help' (L536, T362, R636, B390)
| ['&Help', '&HelpButton', 'Button5']
| child_window(title="&Help", class_name="Button")
|
| Static - '' (L196, T364, R198, B366)
| ['ReplaceStatic', 'Static3']
| child_window(class_name="Static")
(7) >>> app.Replace.Cancel.click()
(8) >>> app.UntitledNotepad.Edit.type_keys("Hi from Python interactive prompt %s" % str(dir()), with_spaces = True)
<pywinauto.controls.win32_controls.EditWrapper object at 0x00DDC2D0>
(9) >>> app.UntitledNotepad.menu_select("File -> Exit")
(10) >>> app.Notepad.DontSave.click()
>>>
~~~
1. 導入pywinauto.application模塊(通常是您需要直接導入的唯一模塊)
2. 創建一個Application實例。 對該應用程序的所有訪問都是通過該對象完成的。
3. 我們在步驟2中創建了一個Application實例,但是我們沒有提供它所引用的Windows應用程序的任何信息。 通過使用start()方法,我們執行該應用程序并將其連接到Application實例應用程序。
4. 在記事本對話框周圍繪制一個綠色矩形 - 這樣我們就知道我們有正確的窗口。
5. 從應用程序所連接的應用程序的“記事本”對話框的“編輯”菜單中選擇“替換”項。 此操作將顯示“替換”對話框。
6. 在“替換”對話框上打印控件的標識符,例如,“替換”對話框上的第一個編輯控件可以通過以下任何標識符引用:
~~~
app.Replace.Edit
app.Replace.Edit0
app.Replace.Edit1
app.FindwhatEdit
~~~
最后一個是讓用戶在閱讀腳本后最好了解腳本的功能。
7. 關閉“替換”對話框。 (在腳本文件中,使用close\_click()而不是click()更安全,因為close\_click()會等待一段時間以給窗口時間關閉對話框。)
8. 讓我們在記事本文本區域中鍵入一些文本。 沒有`with_spaces`參數,就不會輸入空格。 請參閱SendKeys的文檔,了解此方法,因為它是SendKeys的一個薄包裝器。
9. 要求退出記事本
10. 我們將被詢問是否要保存 - 單擊“否”按鈕。
- 什么是Pywinauto
- 入門指南
- 如何
- 等待長時間操作
- 遠程執行指南
- 每種不同控制類型可用的方法
- 貢獻者
- 開發筆記
- 待辦項目
- 更新日志
- 基本用戶輸入模塊
- pywinauto.mouse
- pywinauto.keyboard
- 主要用戶模塊
- pywinauto.application
- pywinauto.findbestmatch
- pywinauto.findwindows
- pywinauto.timings
- 特定功能
- pywinauto.clipboard
- pywinauto.win32_hooks
- 控件參考
- pywinauto.base_wrapper
- pywinauto.controls.hwndwrapper
- pywinauto.controls.menuwrapper
- pywinauto.controls.common_controls
- pywinauto.controls.win32_controls
- pywinauto.controls.uiawrapper
- pywinauto.controls.uia_controls
- Pre-supplied Tests
- pywinauto.tests.allcontrols
- pywinauto.tests.asianhotkey
- pywinauto.tests.comboboxdroppedheight
- pywinauto.tests.comparetoreffont
- pywinauto.tests.leadtrailspaces
- pywinauto.tests.miscvalues
- pywinauto.tests.missalignment
- pywinauto.tests.missingextrastring
- pywinauto.tests.overlapping
- pywinauto.tests.repeatedhotkey
- pywinauto.tests.translation
- pywinauto.tests.truncation
- 后端內部實施模塊
- pywinauto.backend
- pywinauto.element_info
- pywinauto.win32_element_info
- pywinauto.uia_element_info
- pywinauto.uia_defines
- 內部模塊
- pywinauto.controlproperties
- pywinauto.handleprops
- pywinauto.xml_helpers
- pywinauto.fuzzydict
- pywinauto.actionlogger
- pywinauto.sysinfo
- pywinauto.remote_memory_block