利用pywinauto 打造一個Win視窗自動化流程 這一陣子因為需要頻繁連入公司VPN處理事務 實在懶得按每個鈕, 就寫Script完成重複的任務.
環境安裝
測試01 - 記事本另存新檔 按照 官方說明文件 先對notepad做了一番操作, 大概理解其中的工作原理. 官方很貼心地送了一套 gui-inspect-tool工具包 認真說, 真的沒有需要, 全部操作可以靠 backend="uia"
以及 print_control_identifiers()
搭配操作, 在command視窗中完成
''' File name: main.py Author: zswang Date created: 11/25/2021 Date last modified: 11/25/2021 Python Version: 3.10.0 64-bit This file open notepad and save it in "HH_MM_SS.txt" format, pywinauto test code 01 ''' from pywinauto.application import Applicationfrom datetime import datetimesTime = datetime.today().strftime('%H_%M_%S' ) """ 直接開新的instance並連接上,此處使用Win32 API """ app = Application(backend="uia" ).start("notepad.exe" ) app.Notepad.wait('ready' ) """ 按下檔案的另存新檔 """ app.Dialog.menu_select("檔案(&F)->另存新檔(&A)" ) """ 尋找檔案名稱Edit, 將日期輸入""" app.Dialog.Edit.set_edit_text(sTime) app.Dialog.Button.Click() app.Kill_()
分析視窗 如果使用uia方式開起, 並使用 app.top_window().print_control_identifiers()
查看 會出現以下格式的控制項(Control)清單
Dialog - '未命名 - 記事本' (L316, T338, R2368, B1598) ['Dialog', '未命名 - 記事本', '未命名 - 記事本Dialog'] child_window(title="未命名 - 記事本", control_type="Window") | | Edit - '文字編輯器' (L329, T435, R2355, B1545) | ['Edit'] | child_window(title="文字編輯器", auto_id="15", control_type="Edit") | | | | ScrollBar - '垂直' (L2321, T435, R2355, B1511) | | ['ScrollBar', '垂直ScrollBar', '垂直', 'ScrollBar0', 'ScrollBar1'] | | child_window(title="垂直", auto_id="NonClientVerticalScrollBar", control_type="ScrollBar") | | | | | | Button - '上移一行' (L2321, T435, R2355, B469) | | | ['Button', '上移一行', '上移一行Button', 'Button0', 'Button1'] | | | child_window(title="上移一行", auto_id="UpButton", control_type="Button") | | | | | | Button - '下移一行' (L2321, T1477, R2355, B1511) | | | ['下移一行', 'Button2', '下移一行Button'] | | | child_window(title="下移一行", auto_id="DownButton", control_type="Button")
以最高層級的控制項來說明,
Dialog - '未命名 - 記事本' (L316, T338, R2368, B1598)
代表其控制項class為 Dialog 內容名稱為 ‘未命名 - 記事本’ 左上右下距離分別為 (L316, T338, R2368, B1598) 譬如說這樣操作可以獲得名稱資訊
['Dialog', '未命名 - 記事本', '未命名 - 記事本Dialog']
為可以使用的控制項名稱(官方稱作best_match) 譬如我們可以這樣操作
app.Dialog.menu_select("檔案(&F)->另存新檔(&A)" ) app['Dialog' ].menu_select("檔案(&F)->另存新檔(&A)" ) app['未命名 - 記事本Dialog' ].menu_select("檔案(&F)->另存新檔(&A)" )
child_window(title="未命名 - 記事本", control_type="Window")
最後這一行很貼心的附上了可以直接拿來操作的語句
app.child_window(title="上移一行" , auto_id="UpButton" , control_type="Button" ).click()
實戰 - 設定->VPN
人因懶惰而偉大
這對於軟體工程師來說是亙古不變的道理
環境: windows 11 app: PulseSecure
from pywinauto.application import Applicationimport osimport timeos.system("start ms-settings:network-vpn" ) time.sleep(1 ) app = Application(backend="uia" ).connect(title_re="設定" , class_name="ApplicationFrameWindow" ) dlg = app.Dialog[u'WHQVPN 1' ] dlg.child_window(title="連線" , control_type="Button" ).click() time.sleep(1 ) Checkbox_object = dlg.child_window(title="Proceed" , control_type="CheckBox" ).wrapper_object() Checkbox_object.get_toggle_state() Checkbox_object.toggle() dlg.child_window(title="下一步" , control_type="Button" ).click() if any ('無法連線' in s for s in app.Dialog['WHQVPN 1' ].Static3.texts()): print("無法連線(●'◡'●)" ) app.Kill_() dlg.ComboBox.select("Non O365 User" ) dlg.Button2.click() dlg.Edit.set_edit_text("員工編號" ) dlg.Edit2.set_edit_text("員工密碼" ) dlg.child_window(title="下一步" , control_type="Button" ).click() dlg.Edit.set_focus() while True : if (len (dlg.Edit.texts()[0 ]) == 6 ): dlg.child_window(title="下一步" , control_type="Button" ).click() break time.sleep(1 ) time.sleep(1 ) if dlg.child_window(title="工作階段選擇" , control_type="ComboBox" ).exists(): dlg.child_window(title="下一步" , auto_id="NextButton" , control_type="Button" ).click() while True : if app.Dialog['WHQVPN 1' ].child_window(title="已連線" , control_type="Text" ).exists(): break time.sleep(1 ) app.Kill_()
Reference Pywinauto 自動控制windows