0%

day10-pywinauto系統自動化

利用pywinauto打造一個Win視窗自動化流程
這一陣子因為需要頻繁連入公司VPN處理事務
實在懶得按每個鈕, 就寫Script完成重複的任務.

環境安裝

pip install -U pywinauto

測試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 Application
from datetime import datetime

sTime = 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)")
## 數字Item法
# items = app.Notepad.menu().items()
# app.notepad.menu().item(0).sub_menu().item(4).click()
## 如果用uia開啟, 可以使用keyboaard法
# app['Dialog']['應用程式Menu'].MenuItem1.type_keys("{DOWN 4}{ENTER}")

""" 尋找檔案名稱Edit, 將日期輸入"""
## 獲取另存檔案 window 各種 control
# app.top_window().print_control_identifiers()
## 風騷操作, 選擇儲存位置
# app.Dialog.SysTreeView32.GetItem(u"\我的最愛\下載").ClickInput()
## 在樹狀圖中找到 檔案名稱 控制項, 叫做 Edit, 填入時間
app.Dialog.Edit.set_edit_text(sTime)
## 按下 存檔(&S) 按鈕
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)
譬如說這樣操作可以獲得名稱資訊

app.Dialog.texts()

['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 Application
import os
import time

os.system("start ms-settings:network-vpn")
time.sleep(1)

## 連接上instance,此處使用MS UI Automation
app = Application(backend="uia").connect(title_re="設定", class_name="ApplicationFrameWindow")
## 開始用 print_control_identifiers 慢慢找每個按鈕
# app.Dialog['WHQVPN 1'].print_control_identifiers()

## 按下 WHQVPN 連線 按鈕
dlg = app.Dialog[u'WHQVPN 1']
dlg.child_window(title="連線", control_type="Button").click()
time.sleep(1)
## Proceed 按鈕打勾, 需要特別取得object, 按下一步
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_()

## 選擇 Non O365 user , 按下一步
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()

## 輸入pin碼, 先將其focus, 待使用者輸入6位後就按下一步
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()
## check if 已連線 control exist
while True:
if app.Dialog['WHQVPN 1'].child_window(title="已連線", control_type="Text").exists():
break
time.sleep(1)
app.Kill_()

Reference

Pywinauto 自動控制windows