python pywinauto使用過程及問題小結
1- pywinauto 中的uia是什么意思?
在
pywinauto
庫中,uia
指的是UI Automation,這是Windows操作系統(tǒng)提供的一種技術框架,用于實現(xiàn)用戶界面(UI)的自動化測試和輔助功能訪問。UI Automation是微軟從Windows Vista開始引入的核心技術,旨在為所有UI元素提供一致的編程接口,無論這些元素是由何種技術(如Win32、WPF、UWP等)實現(xiàn)的。在
pywinauto
中,通過指定backend="uia"
,用戶可以選擇使用UI Automation作為底層引擎來與應用程序的UI元素進行交互。這為自動化測試提供了更廣泛的支持,尤其是在處理現(xiàn)代Windows應用(尤其是使用WPF和UWP技術構建的應用)時更為有效。UI Automation后端相比傳統(tǒng)的win32
后端,提供了更豐富和高級的元素屬性、控制模式(Control Patterns)以及事件支持,使得自動化腳本可以更加靈活和強大。使用UI Automation后端,開發(fā)者可以更容易地定位和操作UI元素,如獲取元素的屬性、模擬鼠標和鍵盤操作、監(jiān)聽UI事件等,這對于創(chuàng)建復雜的自動化測試場景尤其有用。
2-pywinauto 中main_window.child_window()方法及參數(shù)詳細介紹
在pywinauto
中,child_window()
方法是用于定位父窗口中的一個特定子窗口或控件。這對于自動化測試中準確地找到并操作特定的UI元素至關重要。下面是對child_window()
方法及其參數(shù)的詳細說明:
基本用法
child_window()
方法屬于窗口對象,用于在當前窗口的所有子窗口中搜索符合指定條件的第一個子窗口?;菊Z法如下:
Python
1parent_window.child_window(arguments)
其中,parent_window
是你已經獲取到的窗口對象,arguments
是用于篩選子窗口的一系列關鍵字參數(shù)。
參數(shù)詳解
child_window()
方法接受多個關鍵字參數(shù)來精確地定位子窗口,常見的參數(shù)包括但不限于:
- title: 字符串,控件的標題或文本。
- class_name: 字符串,控件的類名。
- control_type: 字符串,控件的類型(僅在UIA backend下有效)。
- auto_id: 字符串,控件的自動化ID(AutomationId)。
- name: 字符串,控件的名稱屬性,可能與標題或自動化ID不同。
- backend: 字符串,指定后端類型,如
'win32'
或'uia'
,默認自動選擇。 - process: 整數(shù),指定目標窗口所在的進程ID。
- enabled: 布爾值,是否只查找啟用的控件。
- visible: 布爾值,是否只查找可見的控件。
- top_level_only: 布爾值,是否只在頂級窗口中查找。
- found_index: 整數(shù),當找到多個匹配項時,可以選擇特定索引的匹配項。
- control_id: 整數(shù),控件的ID(Windows API中的控件ID)。
- handle: 整數(shù)或
ctypes.c_void_p
,直接指定控件的句柄。 - regex_title: 正則表達式對象或字符串,用于匹配標題的正則表達式。
- regex_class_name: 同上,但用于匹配類名的正則表達式。
- best_match: 字符串,用于
best_match()
邏輯,嘗試匹配最接近的控件。
示例
假設你想在記事本應用的主窗口中找到名為“文件”(File)的菜單項,可以這樣做:
Python
from pywinauto import Application app = Application(backend="uia").start("notepad.exe") main_window = app.window(title="無標題 - 記事本") # 使用class_name和title定位子窗口 menu_item = main_window.child_window(class_name="MenuItem", title="文件")
注意事項
- 參數(shù)組合:通常,為了精確匹配,你需要結合多個參數(shù)一起使用。比如,同時使用
class_name
和title
可以更準確地定位目標控件。 - 性能考量:如果條件設置得過于寬泛,可能會導致搜索緩慢或找到錯誤的控件。盡量使用具體的屬性來縮小搜索范圍。
- 異常處理:如果找不到匹配的子窗口,
child_window()
會拋出異常。可以使用try-except結構來妥善處理這種情況。
通過靈活運用child_window()
方法及其參數(shù),你可以高效地定位并操作UI自動化中的各種元素。
3-child_window() / children() / descendants()方法介紹
如果要獲取父窗口下的所有直接子窗口(而不只是單個子窗口),可以使用children()
方法,它會返回一個包含所有直接子元素的列表。此元素定位方式,需要window類型為: WindowSpecification
例如:
Python
all_direct_children = parent_window.children()
而如果你需要遞歸地獲取所有子孫窗口(子窗口以及子窗口的子窗口等),可以使用descendants()
方法, 此元素定位的方式,需要window類型為:UiaWrapper:
Python
all_descendants = parent_window.descendants()
總結來說,parent_window.child_window()
是用于定位單個滿足特定條件的子窗口,而不是獲取所有子內容。 若要獲取所有子內容,請根據(jù)需要使用children()
或descendants()
。
4-pywinauto打開程序時,程序啟動的比較慢,或點擊按鈕彈窗比較慢,或者打開新窗口窗口渲染的比較慢,怎么添加合理的等待時間
在使用pywinauto
進行自動化操作時,合理地添加等待時間是非常重要的,尤其是在程序啟動慢、按鈕點擊后彈窗延遲或新窗口渲染速度較慢的情況下。這能確保腳本在繼續(xù)執(zhí)行下一步操作前,等待相關的UI元素準備就緒。以下是幾種添加等待時間的方法:
1. 顯式等待(推薦)
使用wait
方法是最靈活且推薦的等待方式,它允許你指定等待的條件和超時時間。
等待窗口可見:
Python
app.Window(title="窗口標題").wait("visible", timeout=10)
等待控件可用:
Python
app.Dialog.child_window(title="按鈕標題", control_type="Button").wait("enabled", timeout=20)
2. 靜態(tài)等待(time.sleep)
雖然不如顯式等待靈活,但在某些簡單場景下可以使用time.sleep
來簡單地暫停腳本執(zhí)行。
Python
import timetime.sleep(5) # 等待5秒
3. 動態(tài)等待
對于某些不確定何時完成的操作,可以在循環(huán)中結合檢查條件和短暫停頓來實現(xiàn)動態(tài)等待。
Python
import time timeout = 30 # 總超時時間 interval = 1 # 檢查間隔 start_time = time.time() while time.time() - start_time < timeout: if app.Window(title="窗口標題").exists(): break time.sleep(interval) else: raise TimeoutError("窗口未在指定時間內出現(xiàn)")
4. 使用backend的特性
某些后端(如uia
)提供了更高級的等待邏輯,例如在啟動應用程序時可以利用Application().start(..., timeout=...)
來指定啟動超時時間。
5-pywinauto 打開的app有多個不同的窗口時,怎么定位具體的某個窗口
在pywinauto
中,如果一個應用程序打開了多個不同的窗口,要定位到具體的某個窗口,你可以根據(jù)窗口的特征來精確篩選。以下是一些常用的篩選方法:
1. 根據(jù)窗口標題(Title)
窗口標題通常是區(qū)分不同窗口最直觀的屬性。你可以使用窗口標題的精確文本或正則表達式來定位窗口。
Python
specific_window = app.window(title="窗口的精確標題") # 或者使用正則表達式來模糊匹配 specific_window = app.window(title_re="窗口標題的一部分.*")
2. 結合類名(Class Name)
如果多個窗口的標題相似,但類名不同,你可以結合類名一起篩選。
Python
specific_window = app.window(title="窗口標題", class_name="窗口類名")
3. 控件類型(Control Type)和自動化ID(Automation ID)
在使用UIA后端時,如果窗口內有特定類型的控件或者控件具有唯一的Automation ID,也可以作為定位依據(jù)。
Python
# 假設窗口內有特定的控件類型或ID specific_window = app.window(control_type="Window", automation_id="特定的自動化ID")
4. 使用子窗口或后代窗口
如果需要定位的窗口是另一個窗口的子窗口或后代,可以先定位到父窗口,再通過child_window()
或descendants()
方法找到目標窗口。
Python
parent_window = app.window(title="父窗口標題") specific_child_window = parent_window.child_window(title="子窗口標題") # 或者遍歷所有后代窗口找到匹配的 specific_descendant_window = parent_window.descendants(title="后代窗口標題")[0] # 注意這會返回一個列表
5. 通過句柄(Handle)
如果你已經知道了窗口的句柄,可以直接通過句柄連接到窗口。
Python
from pywinauto.windows import win32functions handle = win32functions.FindWindowEx(None, None, "窗口類名", "窗口標題") specific_window = app.window(handle=handle)
注意事項
- 在使用
wait
方法確保窗口已經加載和可見,特別是在定位動態(tài)彈出的窗口時,這可以避免因窗口未準備好而導致的錯誤。 - 當有多個窗口匹配相同的篩選條件時,考慮使用更具體的篩選條件或結合多個屬性來定位。
- 如果窗口是在不同進程中創(chuàng)建的,可能需要使用
Application.connect()
方法來單獨連接到那個窗口。
6-pywinauto 中element_info 和BaseWrapper的用法
在pywinauto
中,element_info
和BaseWrapper
是兩個核心概念,它們在自動化測試和界面操作中扮演著重要角色。
element_info (pywinauto.element_info.ElementInfo
)
element_info
是一個底層的、面向元素信息的抽象類,它提供了對UI元素屬性的訪問和描述,但不直接支持操作。它是一個輕量級的包裝,用于存儲和傳遞關于UI元素的信息,如類名、標題、位置等,而不涉及實際的UI操作。element_info
是所有控件信息的基礎,無論控件是通過哪個后端(如win32
, uia
)來識別的。
當你使用pywinauto
的方法(如child_window()
)來定位一個元素時,實際上就是在創(chuàng)建或查找一個element_info
對象。element_info
對象可以被用來創(chuàng)建更高級的包裝對象,如BaseWrapper
。
BaseWrapper (pywinauto.base_wrapper.BaseWrapper
)
BaseWrapper
是基于element_info
的更高層次的包裝,它不僅包含了元素的信息,還提供了對元素進行操作的方法,如點擊、發(fā)送文本、獲取屬性等。當你需要與UI元素進行交互時,比如點擊一個按鈕或填寫一個文本框,你應該使用BaseWrapper
對象。
BaseWrapper
是element_info
的直接擴展,它將底層的元素信息轉換為可操作的對象。這意味著,你可以通過BaseWrapper
直接執(zhí)行如.click()
, .set_text()
, .texts()
, .exists()
等操作,使得自動化腳本編寫更為直觀和便利。
使用示例
假設你要操作一個按鈕,可以按照以下步驟:
Python
from pywinauto import Application app = Application().start("your_app.exe") main_win = app.window(title="Main Window Title") # 使用child_window()方法找到一個按鈕,得到的是一個BaseWrapper實例 button = main_win.child_window(title="Click Me", control_type="Button") # 然后,你可以直接調用BaseWrapper的方法來操作這個按鈕 button.click()
在這個過程中,雖然沒有直接使用element_info
,但實際上child_window()
內部處理了從element_info
到BaseWrapper
的轉換,讓你能夠直接與UI元素進行交互。
7-pywinauto 的BaseWrapper類還有哪些方法,詳細說明
根據(jù)搜索到的內容總結如下
pywinauto
的BaseWrapper
類提供了許多方法來操作和查詢UI元素。這些方法覆蓋了從基本的屬性訪問到復雜的UI交互,下面是BaseWrapper
類中一些常用方法的概覽和簡要說明:
常用屬性和方法 .window_text()
: 獲取控件的文本內容。
.texts()
: 返回一個包含控件所有文本內容的列表,適用于包含多行文本的控件。.class_name()
: 返回控件的類名。.control_type()
: 使用UIA后端時,返回控件的類型。.enabled()
: 檢查控件是否啟用。.visible()
: 檢查控件是否可見。.rectangle()
: 獲取控件的矩形區(qū)域,包括坐標和大小。.set_focus()
: 將焦點設置到控件上。.click_input()
: 模擬鼠標點擊控件。.double_click_input()
: 雙擊控件。.right_click_input()
: 右鍵點擊控件。.drag_drop(target)
: 拖拽控件到指定的目標。.type_keys(keys, with_spaces=True, set_foreground=True)
: 向控件發(fā)送鍵盤輸入。.set_text(text)
: 設置文本控件的內容。.texts()
: 獲取控件的所有文本內容,適用于可能有多行文本的控件。
控件定位和操作擴展
.child_window(**kwargs)
: 在當前控件的子控件中查找并返回匹配條件的BaseWrapper
對象。.descendants(**kwargs)
: 返回當前控件的所有子孫控件的列表,這些控件滿足給定的篩選條件。.parent()
: 返回當前控件的直接父控件的BaseWrapper
對象。.top_parent()
: 返回當前控件的最頂級父控件的BaseWrapper
對象。 屬性獲取與設置.get_properties()
: 獲取控件的所有屬性信息。.set_property(name, value)
: 設置控件的屬性值。 圖像操作.capture_as_image()
: 截取控件的圖像,返回一個PIL圖像對象。 其他.wait('exists', timeout=timedelta(seconds=5))
: 等待控件存在,直到超時。.wait('visible', timeout=timedelta(seconds=5))
: 等待控件變得可見。.wait_not('visible', timeout=timedelta(seconds=5))
: 等待控件不再可見。
以上只列出了部分方法,實際上BaseWrapper
類包含更多功能,涵蓋了大多數(shù)自動化測試和UI操作的需求。具體的使用方法和參數(shù)細節(jié),建議參考pywinauto
的官方文檔或直接查看源代碼注釋,以獲取最全面和最新的信息。
- 嘗試顯式類型轉換:如果適用,可以嘗試將找到的元素轉換為具體的控件類型,然后調用
.click_input()
方法。
Python
from pywinauto.controls.win32_controls import ButtonWrapper button = ButtonWrapper(element) button.click_input()
如果以上方法都不能解決問題,建議查閱 pywinauto
的官方文檔或訪問其GitHub頁面查看是否有相關的問題報告或更新日志。同時,確保你的Python環(huán)境是最新的,并考慮升級 pywinauto
到最新穩(wěn)定版。
8-pywinauto 都可以轉換成什么控件類型Wrapper
pywinauto
提供了一系列針對不同控件類型的Wrapper類,這些類封裝了對特定類型控件的操作方法,使得自動化操作更加直觀和方便。以下是部分常用的控件類型及其對應的Wrapper類:
Button - ButtonWrapper
用于操作按鈕,支持點擊等操作。
Edit - EditWrapper
用于編輯框,支持文本的設置、獲取等操作。
ComboBox - ComboBoxWrapper
用于組合框(下拉列表),支持選擇項、獲取選項列表等。
ListBox - ListBoxWrapper
用于列表框,支持選擇項、獲取項列表等。
CheckBox - CheckBoxWrapper
用于復選框,支持勾選、取消勾選及狀態(tài)檢查。
RadioButton - RadioButtonWrapper
用于單選按鈕,支持選擇及狀態(tài)檢查。
Menu - MenuWrapper
用于菜單項,支持展開菜單、選擇菜單項等。
TreeView - TreeViewWrapper
用于樹狀視圖控件,支持節(jié)點的展開、選擇等。
TabItem - TabWrapper
用于選項卡控件中的單個選項卡。
Dialog - DialogWrapper
對話框窗口的特殊處理,雖然也是窗口,但可能包含特定于對話框的操作方法。
Window - WindowWrapper
通用窗口類,幾乎所有窗口都可以用這個類來操作,但針對特定控件類型使用特定的Wrapper會更方便。
請注意,直接將element_info
轉換為特定的Wrapper類(如從element_info
到ButtonWrapper
)并不總是直接支持,通常你不需要手動進行這樣的轉換。當你通過正確的篩選條件使用child_window()
等方法時,pywinauto
會自動為你提供最適合的Wrapper實例。如果需要更具體的控件操作,可以通過訪問BaseWrapper
實例的屬性或方法來實現(xiàn),因為大多數(shù)Wrapper類都是從BaseWrapper
繼承而來,已經包含了基礎的交互方法。
在實際使用中,直接利用child_window()
方法根據(jù)控件的屬性(如標題、類名、控制類型等)來定位并操作控件即可,大多數(shù)情況下無需關心底層的Wrapper類轉換。如果需要更高級或特定的功能,查閱pywinauto
的文檔或源碼,了解特定Wrapper類的使用方法會更有幫助。
9-pywinauto打印獲取的windows對象,一個是加了等待,一個是沒加等待。
獲取的window對象使用print(window),打印出來分別是uiawrapper.UIAWrapper - 'New mail', Dialog,另一個是<pywinauto.application.WindowSpecification object at 0x0000016B0DE556A0> 有什么區(qū)別
在使用pywinauto
時,你觀察到的區(qū)別反映了兩種不同狀態(tài)的窗口對象處理方式。
未使用等待(直接獲?。?/p>
Python
window = app.window(title="New mail") print(window)
輸出類似于 <pywinauto.application.WindowSpecification object at 0x0000016B0DE556A0>
,這表明你獲得的是一個WindowSpecification
對象。這個對象實際上是一個窗口的描述符或者說規(guī)范,它定義了你要找的窗口應該滿足的條件(在這個例子中是標題為"New mail"的窗口)。但是,此時pywinauto
尚未實際找到并實例化這個窗口,這意味著打印出來的信息是一個內存地址,代表了這個待查找窗口描述的實例。
使用等待后獲取
Python
window = app.window(title="New mail").wait('visible') print(window)
或者
Python
window = app.window(title="New mail").wait('exists', timeout=10)
輸出為 uiawrapper.UIAWrapper - 'New mail', Dialog
,這表示你成功獲取到了一個實際的UI元素對象,它已經不是一個簡單的查找規(guī)范,而是窗口實際存在的、可操作的對象。uiawrapper.UIAWrapper
是pywinauto
內部用于包裝UIA(UI Automation)元素的類,這意味著窗口不僅被找到了,而且已經準備好了進行進一步的交互操作,比如點擊、輸入文本等。
區(qū)別總結
- 未使用等待:你得到的是一個窗口的查找描述符,它定義了要找什么,但不代表窗口已被找到或實例化。
- 使用等待:你得到的是一個實際的UI元素對象,它代表了已經找到的窗口,可以直接用于執(zhí)行各種操作。
在實際應用中,使用等待(如.wait('visible')
或.wait('exists')
)通常更為推薦,因為它確保了操作不會在窗口還沒準備好時進行,提高了腳本的穩(wěn)定性和可靠性。
10-我的pywinauto程序,Application.start()后,還需要執(zhí)行application.connect() 后的app ,才能正常使用。否則此app獲得的windows不可用,為什么?
在pywinauto
中,理論上,一旦你使用Application.start()
方法啟動了一個應用程序,它應該自動連接到這個新啟動的應用實例,并且你能夠直接通過返回的app
實例進行窗口和控件的查找及操作,無需再次調用app.connect()
。
如果遇到必須先執(zhí)行app.connect()
后才能正常使用app
獲得的窗口或控件,這可能由幾個原因引起:
- 啟動時間問題:應用程序可能需要一段時間才能完全初始化其窗口和UI元素。即便
start()
方法在內部嘗試連接,但如果UI還未完全加載,后續(xù)立即進行窗口或控件查找可能失敗。此時,手動調用connect()
給予額外的時間緩沖,可能恰好讓UI準備就緒。 - 后臺啟動或隱藏窗口:某些應用程序可能在啟動時沒有立即顯示窗口,或者窗口最小化到托盤/后臺運行,導致初次連接時無法正確識別窗口。稍后調用
connect()
可能窗口已變?yōu)榭梢娀蚩山换顟B(tài)。 - UI自動化框架的兼容性或bug:盡管少見,但不同版本的
pywinauto
或目標應用程序的UI框架(如WPF、UWP等)可能存在兼容性問題,導致自動連接行為不如預期。 - 多實例問題:如果你的應用程序允許或意外啟動了多個實例,
start()
可能連接到了不同的實例,而你期望操作的窗口位于另一個實例中。再次手動指定連接條件確保連接到正確的窗口實例。
解決方法建議:
- 增加啟動后等待:在
start()
后,加入適當?shù)牡却龝r間(如time.sleep()
),讓應用程序有足夠的時間初始化窗口和UI元素。 - 精確連接:確保
connect()
時使用足夠的參數(shù)(如窗口標題、類名等)來精確匹配你想要連接的窗口,避免連接到錯誤的實例或窗口。 - 檢查
pywinauto
版本:確保你使用的pywinauto
版本是最新的,以獲取最佳的兼容性和穩(wěn)定性。 - 日志和調試:使用
pywinauto
的日志功能或打印調試信息,檢查啟動和連接過程中可能出現(xiàn)的任何異?;蚓?,幫助定位問題所在。
如果問題持續(xù),考慮查看pywinauto
的官方文檔或社區(qū),可能有針對特定應
用或類似問題的解決方案。
我的問題其實應該是兼容問題,我增加start()后的等待時間,無效。
必須使用Application.connect()命令,重新獲取app后,只有的操作才有效。
app = Application(backend="uia").start(outlook_path, timeout=30) # 等待也是必須的 time.sleep(15) # 必須使用connect()重新生成 app = Application(backend="uia").connect(class_name="Olk Host") print("outlook第一次啟動,打開outlook成功!, app=", app)
11-Application(backend="uia").start()參數(shù)中有timeout參數(shù)嗎?
沒有。
因為在標準的pywinauto
文檔和通常的使用中,直接的start()
方法調用并不直接接受一個timeout
參數(shù)用于等待應用程序啟動完成。
通常,如果你想控制應用程序啟動后的等待邏輯或處理超時,你需要采取一些額外的步驟,而不是直接通過start()
方法的timeout
參數(shù)(因為這個參數(shù)在標準用法中不存在)。以下是一些建議:
- 手動等待:在
start()
調用之后,你可以使用time.sleep()
或更復雜的邏輯來等待一段預估的合理時間,確保應用程序有足夠的時間啟動并加載完畢。 - 動態(tài)等待窗口:利用
pywinauto
的等待機制,比如top_window()
或wait()
方法,來動態(tài)等待直到應用程序的某個窗口變得可用或滿足特定條件。例如,如果你知道應用程序啟動后主窗口的標題,可以這樣做:
Python
app = Application(backend="uia").start("your_app_path") main_window = app.window(title="Your Main Window Title").wait("visible", timeout=30)
這里,timeout=30
就是等待窗口變?yōu)榭梢姷淖畲髸r間,單位為秒。
異常處理:在嘗試訪問應用程序窗口或執(zhí)行操作時,使用try-except結構來捕獲可能的超時或失敗異常,從而實現(xiàn)間接的超時控制。
自定義邏輯:根據(jù)應用程序的具體行為,你可能需要更定制化的邏輯來確定何時應用程序已成功啟動并準備好進行自動化操作,這可能涉及檢查特定進程的狀態(tài)、窗口屬性變化等。
綜上所述,雖然直接在Application.start()
方法中設置timeout
參數(shù)不是標準用法,但通過上述方法你可以有效地控制和管理應用程序啟動后的等待邏輯及超時處理。
12-Application(backend="uia").connect()參數(shù)中有timeout參數(shù)嗎?
沒有。
但pywinauto
提供了靈活的等待和超時控制機制來確保連接操作不會無限期地阻塞。
自動化等待與超時控制
雖然直接在connect()
方法簽名中沒有公開timeout
參數(shù),但pywinauto
的wait
系列方法和自動化操作通常允許你指定一個超時限制,例如在嘗試連接到一個窗口時:
Python
app = Application(backend="uia") main_window = app.window(title="Your App Title").wait("exists", timeout=30)
在這個例子中,wait("exists", timeout=30)
是嘗試等待窗口存在最多30秒。如果窗口在指定時間內變得可訪問,則繼續(xù)執(zhí)行;如果超時,則會拋出異常,允許你的腳本據(jù)此作出反應。
定制等待邏輯
對于更復雜的場景,你可能需要自定義等待邏輯,比如檢查窗口是否不僅存在還滿足其他條件(如“visible”、“enabled”等),并結合自定義的循環(huán)和超時處理。
總結
盡管Application(backend="uia").connect()
直接調用時不帶有一個明確的timeout
參數(shù),pywinauto
提供的wait
方法和自動化操作的靈活性讓你能夠有效控制連接和等待窗口出現(xiàn)的時間限制,實現(xiàn)類似超時處理的邏輯。通過這種方式,確保了自動化腳本既能夠穩(wěn)健地等待必要的窗口準備就緒,又能避免不必要的阻塞。
13-彈窗操作
在pywinauto
中,當需要連接并操作一個應用程序新彈出的窗口時,通常需要先確保新窗口已經出現(xiàn),然后通過窗口的標題或其他屬性來定位并操作它。以下是處理新彈窗的基本步驟:
1. 等待彈窗出現(xiàn)
使用pywinauto
的等待功能確保彈窗已經打開。這可以通過循環(huán)檢查窗口是否存在,或者使用更高級的等待方法來實現(xiàn)。
Python
from pywinauto import Application from pywinauto.timings import wait_until_passes app = Application().connect(title="主應用程序的標題") # 首先連接到主應用程序 def is_popup_opened(): try: app.window(title="彈窗的標題") return True except Exception: return False # 等待彈窗出現(xiàn),最多等待10秒 wait_until_passes(10, 1, is_popup_opened)
2. 定位并操作彈窗
一旦確認彈窗已經打開,就可以通過其標題或其它屬性來定位并進行操作。
Python
popup_window = app.window(title="彈窗的標題") # 例如,點擊彈窗上的一個按鈕 button_on_popup = popup_window.child_window(title="按鈕的標題或自動化ID", control_type="Button") button_on_popup.click()
3. 異常處理和重試機制
在實際操作中,可能需要添加適當?shù)漠惓L幚磉壿?,以防窗口標題變化或打開速度過慢等情況。
4. 關閉彈窗
如果操作完成后需要關閉彈窗,可以調用窗口的關閉方法:
Python
popup_window.close()
注意事項:
- 確保窗口標題是唯一的,或者足夠精確以避免誤操作其他窗口。
- 使用
control_type
、class_name
等屬性可以幫助更精確地定位控件。 - 如果彈窗屬于同一個應用程序,通常不需要再次調用
Application().connect()
,直接在已連接的應用實例上操作即可。 - 根據(jù)彈窗出現(xiàn)的時機和應用的具體行為,可能需要微調等待邏輯。
14-AttributeError: 'UIAWrapper' object has no attribute 'print_control_identifiers'
在pywinauto
中,如果你遇到AttributeError: 'UIAWrapper' object has no attribute 'print_control_identifiers'
錯誤,這意味著你嘗試在一個基于UIA(UI Automation)的控件上使用了print_control_identifiers
方法,但這個方法實際上并不直接存在于UIAWrapper
對象中。
print_control_identifiers
方法主要用于經典的Windows應用程序自動化(win32應用程序),它在Win32Window
對象中可用。然而,對于基于UI Automation(UIA)的應用,如現(xiàn)代的UWP應用或使用了UIA框架構建的桌面應用,直接使用print_control_identifiers
會報錯。
對于UIAWrapper對象,你可以使用.legacy_properties()
方法來獲取控件的部分信息,但這通常不如print_control_identifiers
那樣詳盡。
解決方案:
- 檢查應用類型:確認你的應用是否是現(xiàn)代UIA應用還是傳統(tǒng)的win32應用。如果是UIA應用,繼續(xù)閱讀下文。
- 使用UIA屬性:對于UIAWrapper對象,你可以利用
.properties
屬性來查看控件的屬性。例如:
Python
1control = app.window(title="Your Window Title").child_window(auto_id="Your Control AutoId") 2print(control.properties)
這將打印出控件的屬性字典,其中包含了控制類型、名稱、位置等信息。
自定義打印函數(shù):如果需要類似print_control_identifiers
的詳細輸出,你可能需要自己編寫一個函數(shù)來遍歷.properties
或使用UIA的其他方法來獲取更詳細的控件信息。
降級為win32:在某些情況下,如果應用同時支持UIA和win32,你可以嘗試降級到win32模式來使用print_control_identifiers
。但這取決于應用的具體實現(xiàn)和支持情況,且不是所有UIA應用都能這么做。
記住,UI Automation和經典Windows應用程序自動化在pywinauto
中有一些差異,因此在處理不同類型的窗口和控件時,要相應地調整你的方法。
15-AttributeError: 'UIAWrapper' object has no attribute 'child_window'
UIAWrapper 窗口類型, 定位元素需要使用如下方法定位:ele = setWindow.descendants(title="Remove")[0]
當你在使用pywinauto
遇到AttributeError: 'UIAWrapper' object has no attribute 'child_window'
錯誤時,這意味著你嘗試在一個基于UIA(UI Automation)的控件(UIAWrapper
對象)上調用了child_window
方法,但這個方法在UIAWrapper對象中并不可用。
UIAWrapper對象主要對應于使用UI Automation技術的應用程序,與傳統(tǒng)的Win32應用程序使用的API有所不同。在UIAWrapper上,直接查找子窗口或控件的方法不是child_window
,而是使用更符合UIA查詢方式的屬性和方法。
解決方案:
- 使用
.descendants
方法:對于UIAWrapper對象,可以使用.descendants
方法來查找所有子孫控件,然后通過條件過濾找到目標控件。
Python
controls = window.descendants(control_type="ControlType") # 替換"ControlType"為實際控件類型,如"Button", "Edit",
直接屬性訪問:如果已知某些屬性(如 name
, control_type
等),可以直接通過屬性篩選。
Python
target_control = window.child_window(auto_id="目標控件的AutomationId")
注意:上述child_window
方法是針對經典的win32應用程序,對于UIA,你應該使用更符合其查詢機制的方法,如.descendants
或直接屬性篩選。
構造查詢條件:使用更復雜的查詢條件來精確定位控件,例如使用.filter()
方法。
Python
1from pywinauto.controls.uia_controls import UIAWrapper 2control = UIAWrapper(handle=你的窗口句柄).filter(control_type="ControlType")[0] # 替換"ControlType"和條件
確保理解你的應用是基于UI Automation還是傳統(tǒng)的Win32,然后選擇相應的API和方法來操作和查找控件。對于UIA應用,利用.descendants
和屬性篩選通常是最直接有效的方式。
16-pywinauto,已經找到窗口了,但使用window的方法print_control_identifiers()方法,會提示匹配到多個元素呢?There are 5 elements that match the criteria {'backend': 'uia', 'process': 22188}
當您已經成功定位到一個窗口,但在嘗試使用 window.print_control_identifiers()
方法時遇到提示匹配到多個元素,這通常意味著您調用 print_control_identifiers()
的對象實際上是一個窗口集合或者是基于某些寬泛條件的窗口規(guī)范(WindowSpecification
),而不是一個特定的窗口實例。
為了解決這個問題,確保您在調用 print_control_identifiers()
之前,已經準確地指定了一個唯一的窗口。這里有幾個步驟可以幫助您:
- 重新檢查定位窗口的代碼:確認您是否確實已經通過足夠精確的條件定位到單一窗口。比如,使用窗口的標題、類名、進程ID等屬性來精確篩選。
- 使用更具體的選擇器:如果可能,增加篩選條件,比如使用正則表達式匹配窗口標題 (
title_re
) 或是直接使用窗口的自動化ID (auto_id
)。 - 確保調用鏈正確:如果通過一系列操作到達某個窗口,確認每一步都正確返回了預期的窗口對象。有時,錯誤的調用順序可能導致返回的是窗口集合而非單個窗口。
- 檢查是否使用了模糊匹配:避免使用可能匹配到多個窗口的過于寬泛的條件,如只使用
backend
和process
作為篩選條件。 - 顯式地獲取窗口實例:在確定了窗口后,可以嘗試顯式地通過索引或其他方法獲取具體的窗口實例。例如,如果您知道目標窗口是匹配條件下的第一個,可以嘗試:
Python
specific_window = windows[0] # 假設'windows'是一個包含多個匹配窗口的列表 specific_window.print_control_identifiers()
- 使用
top_level_only=False
:如果目標窗口不是頂級窗口,嘗試設置top_level_only=False
來搜索所有級別的窗口,然后進一步篩選。 - 調試與分析:如果以上方法都無法解決問題,可以先使用
print_control_identifiers()
的depth=-1
參數(shù)來打印所有窗口的信息,幫助您識別哪些窗口與您的預期不符,并據(jù)此調整選擇條件。
確保每個步驟都盡可能具體和精確,以避免匹配到多個窗口的情況發(fā)生。
17-pywinauto, 使用Desktop(backend="uia")獲取窗口
注意點1: Desktop(backend="uia").windows()/window(),兩個方法,只能查詢所有的主窗口,不支持查詢主窗口下都子窗口。 注意點2: 使用Desktop(backend="uia").windows()/window(),如果條件不能定位到窗口的情況下。 Desktop(backend="uia").windows()方法,結果返回空列表。 如下: wins = Desktop(backend="uia").windows(title="Work or school account", class_name="Windows.UI.Core.CoreWindow") print(wins) 結果:[] Desktop(backend="uia").window()方法,結果返回WindowSpecification對象。 如下: win = Desktop(backend="uia").window(title="Work or school account", class_name="Windows.UI.Core.CoreWindow") print(win) 結果:<pywinauto.application.WindowSpecification object at 0x000001524BE87790>,不能使用返回的window()的返回值做判斷。 注意點3: 如果窗口類型為:WindowSpecification,使用parent_window.child_window() 方式查詢窗口元素。 如果窗口類型為:UIAWrapper,使用parent_window.parent_window.descendants() 方式查詢窗口元素。
18-pywinauto ,通過元素的 element.rectangle() 方法,使用pyautogui,移動鼠標到元素位置,實現(xiàn)。
具體寫法如下:
# 假設你想對窗口內的某個元素進行操作,首先找到該元素 element = main_window.child_window(title="Your Element Title", control_type="ControlTypeHere") # 請?zhí)鎿Q為實際的標題和控件類型 # 獲取元素的矩形信息 element_rectangle = element.rectangle() # 使用 pyautogui 將鼠標移動到該位置 x, y = element_rectangle.left + (element_rectangle.width() // 2), element_rectangle.top + (element_rectangle.height() // 2) pyautogui.moveTo(x, y) # 移動到元素中心位置,也可以直接移到左上角,去掉加法部分即可
注意:element_rectangle.width 和element_rectangle.height返回的類型都是method;計算位置坐標是應使用element_rectangle.width() 和element_rectangle.height()形式。
否則報錯:TypeError: unsupported operand type(s) for //: 'method' and 'int'(不支持這兩兩種類型使用//語法計算)
19-編碼過程中,注意循環(huán)引用問題
E ImportError: cannot import name 'assertTrue' from partially initialized module 'assertTool' (most likely due to a circular import) --很可能是循環(huán)導入
問題:“文件a” 導入了 “文件b” 中的方法;“文件b” 也導入了“文件a”中的方法,導致循環(huán)導入問題。
20-pywinauot, 定位uiaWrapper 元素時,增加等待有時候不能解決問題。但是窗口最大化后,就能查找到元素了。
可能的原因:窗口布局變化
應用程序窗口在最大化之前和之后,其內部元素的布局可能會有細微的變化。這種布局變化可能會影響到元素的可訪問性或者導致元素的屬性(如坐標位置)發(fā)生變化。最大化窗口可能使得元素穩(wěn)定地出現(xiàn)在預期的位置或狀態(tài),從而變得可被pywinauto
識別。
21-pywinauto + pytest 執(zhí)行的測試用例時,提示錯誤: Windows fatal exception: code 0x8001010d。 但是如果把此用例的內容,都放到一個文件中執(zhí)行,不通過pytest,就沒有這個錯誤提示。應該怎么解決
錯誤代碼 0x8001010d
通常與COM(Component Object Model)錯誤相關,特別是“RPC服務器不可用”或“接口調用中出現(xiàn)錯誤”。在使用pywinauto
進行GUI自動化測試時遇到這個問題,可能的原因有幾個方面,尤其是在結合pytest
使用時,可能與測試環(huán)境的初始化、資源釋放、并發(fā)執(zhí)行測試等方面有關。以下是一些可能的解決方案:
- 確保單例模式: 確保在整個測試套件中,對應用程序實例的使用是單例的,避免多個測試用例同時操作同一個應用程序實例,這可能導致資源沖突??梢栽?code>conftest.py中使用
fixture
來管理應用實例的生命周期。 - 資源清理: 確保每個測試用例結束后,正確地關閉和清理所有打開的窗口和資源。使用
try...finally
塊或者pytest.fixture
的yield
語句來確保即使測試失敗也能執(zhí)行清理操作。 - 并發(fā)執(zhí)行問題: 如果你的測試套件配置為并行執(zhí)行(pytest默認可能不會,但通過
-n NUM
參數(shù)可以設置),那么可能需要禁用并行執(zhí)行,因為某些GUI操作或COM對象可能不支持多線程或并發(fā)訪問。可以在運行pytest時加上-n 1
參數(shù)來禁用并行執(zhí)行。 - COM對象生命周期管理: 確保所有的COM對象在使用完畢后都被適當釋放。在
pywinauto
中,通常這會自動處理,但如果涉及到自定義COM調用,請確保遵循正確的釋放流程。 - 增加異常處理: 在測試代碼中增加對特定異常的捕獲和處理邏輯,尤其是與COM相關的異常,可以幫助診斷問題所在。可以嘗試捕獲并打印詳細的錯誤信息,以便于進一步分析。
- 檢查環(huán)境差異: 確認直接運行文件和通過pytest運行時的環(huán)境(如環(huán)境變量、權限等)是否一致。有時候,環(huán)境差異也會導致此類問題。
如果以上建議不能解決問題,可能需要更詳細的日志來定位問題??紤]在pytest
運行時增加日志級別(通過-vvv
參數(shù)或配置logging模塊),并查看是否有其他相關的警告或錯誤信息輸出,這有助于更準確地定位問題所在。
相關文章
Python中sorted()函數(shù)之排序的利器詳解
sorted()函數(shù)是Python中的內置函數(shù),用于對可迭代對象進行排序,下面這篇文章主要給大家介紹了關于Python中sorted()函數(shù)之排序的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-08-08python將txt等文件中的數(shù)據(jù)讀為numpy數(shù)組的方法
今天小編就為大家分享一篇python將txt等文件中的數(shù)據(jù)讀為numpy數(shù)組的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12