自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用Python子進程關閉Excel自動化中的彈窗

開發(fā) 后端 自動化
利用Python進行Excel自動化操作的過程中,尤其是涉及VBA時,可能遇到消息框/彈窗(MsgBox)。

[[397615]]

 利用Python進行Excel自動化操作的過程中,尤其是涉及VBA時,可能遇到消息框/彈窗(MsgBox)。此時需要人為響應,否則代碼卡死直至超時 [^1] [^2]。根本的解決方法是VBA代碼中不要出現(xiàn)類似彈窗,但有時我們無權修改被操作的Excel文件,例如這是我們進行自動化測試的對象。所以本文記錄從代碼角度解決此類問題的方法。

假想場景

使用xlwings(或者其他自動化庫)打開Excel文件test.xlsm,讀取Sheet1!A1單元格內容。很簡單的一個操作: 

  1. import xlwings as xw  
  2. wb = xw.Book('test.xlsm')  
  3. msg = wb.sheets('Sheet1').range('A1').value  
  4. print(msg)  
  5. wb.close() 

然而不幸的是,打開工作簿時進行了熱情的歡迎儀式: 

  1. Private Sub Workbook_Open()  
  2.     MsgBox "Welcome"  
  3.     MsgBox "to open"  
  4.     MsgBox "this file."  
  5. End Sub 

第一個彈窗Welcome就卡住了Excel,Python代碼相應卡死在第一行。

基本思路

主程序中不可能直接處理或者繞過此類問題,也不能奢望有人隨時蹲守點擊下一步——那就開啟一個子線程來護航吧。因此,解決方案是利用子線程監(jiān)聽并隨時關閉彈窗,直到主程序圓滿結束。

解決這個問題,需要以下兩個知識點(基礎知識請課外學習):

  •  Python多線程(本文采用threading.Thread)
  •  Python界面自動化庫(本文涉及pywinauto和pywin32)

pywinauto方案

pywinauto顧名思義是Windows界面自動化庫,模擬鼠標和鍵盤操作窗體和控件 [^3]。不同于先獲取句柄再獲取屬性的傳統(tǒng)方式,pywinauto的API更加友好和pythonic。例如,兩行代碼搞定窗口捕捉和點擊: 

  1. from pywinauto.application import Application  
  2. win = Application(backend="win32").connect(title='Microsoft Excel' 
  3. win.Dialog.Button.click() 

本文采用自定義線程類的方式,啟動線程后自動執(zhí)行run()函數(shù)來完成上述操作。具體代碼如下,注意構造函數(shù)中的兩個參數(shù):

  •  title 需要捕捉的彈窗的標題,例如Excel默認彈窗的標題為Microsoft Excel
  •  interval 監(jiān)聽的頻率,即每隔多少秒檢查一次 
  1. # listener.py  
  2. import time  
  3. from threading import Thread, Event  
  4. from pywinauto.application import Application  
  5. class MsgBoxListener(Thread):  
  6.     def __init__(self, title:str, interval:int):  
  7.         Thread.__init__(self)  
  8.         self._title = title   
  9.         self._interval = interval   
  10.         self._stop_event = Event()     
  11.     def stop(self): self._stop_event.set()  
  12.     @property  
  13.     def is_running(self): return not self._stop_event.is_set()  
  14.     def run(self):  
  15.         while self.is_running:  
  16.             try:  
  17.                 time.sleep(self._interval)  
  18.                 self._close_msgbox()  
  19.             except Exception as e:  
  20.                 print(e, flush=True 
  21.     def _close_msgbox(self):  
  22.         '''Close the default Excel MsgBox with title "Microsoft Excel".'''        
  23.          win = Application(backend="win32").connect(title=self._title)  
  24.         win.Dialog.Button.click()  
  25. if __name__=='__main__': 
  26.     t = MsgBoxListener('Microsoft Excel', 3)  
  27.     t.start()  
  28.     time.sleep(10)  
  29.     t.stop() 

于是,整個過程分為三步:

  •  啟動子線程監(jiān)聽彈窗
  •  主線程中打開Excel開始自動化操作
  •  關閉子線程 
  1. import xlwings as xw  
  2. from listener import MsgBoxListener  
  3. # start listen thread  
  4. listener = MsgBoxListener('Microsoft Excel', 3)  
  5. listener.start()  
  6. # main process as before  
  7. wb = xw.Book('test.xlsm')  
  8. msg = wb.sheets('Sheet1').range('A1').value  
  9. print(msg)  
  10. wb.close()  
  11. # stop listener thread  
  12. listener.stop() 

到此問題基本解決,本地運行效果完全達到預期。但我的真實需求是以系統(tǒng)服務方式在服務器上進行Excel文件自動化測試,后續(xù)發(fā)現(xiàn),當以系統(tǒng)服務方式運行時,pywinauto竟然捕捉不到彈窗!這或許是pywinauto一個潛在的問題 [^4]。

win32gui方案

那就只好轉向相對底層的win32gui,所幸完美解決了上述問題。

win32gui是pywin32庫的一部分,所以實際安裝命令是: 

  1. pip install pywin32 

整個方案和前文描述完全一致,只是替換MsgBoxListener類中關閉彈窗的方法: 

  1. import win32gui, win32con  
  2. def _close_msgbox(self):  
  3.     # find the top window by title  
  4.     hwnd = win32gui.FindWindow(None, self._title)  
  5.     if not hwnd: return  
  6.     # find child button  
  7.     h_btn = win32gui.FindWindowEx(hwnd, None,'Button', None)  
  8.     if not h_btn: return  
  9.     # show text  
  10.     text = win32gui.GetWindowText(h_btn) 
  11.     print(text)  
  12.     # click button         
  13.     win32gui.PostMessage(h_btn, win32con.WM_LBUTTONDOWN, None, None)  
  14.     time.sleep(0.2)  
  15.     win32gui.PostMessage(h_btn, win32con.WM_LBUTTONUP, None, None)  
  16.     time.sleep(0.2) 

更一般的方案

更一般地,當同時存在默認標題和自定義標題的彈窗時,就不便于采用標題方式進行捕捉了。例如 

  1. MsgBox "Message with default title.", vbInformation,   
  2. MsgBox "Message with title My App 1", vbInformation, "My App 1"  
  3. MsgBox "Message with title My App 2", vbInformation, "My App 2" 

那就擴大搜索范圍,依次點擊所有包含確定性描述的按鈕(例如OK,Yes,Confirm)來關閉彈窗。同理替換MsgBoxListener類的_close_msgbox()方法(同時構造函數(shù)中不再需要title參數(shù)): 

  1. def _close_msgbox(self): 
  2.     '''Click any button ("OK", "Yes" or "Confirm") to close message box.'''  
  3.     # get handles of all top windows  
  4.     h_windows = []  
  5.     win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), h_windows)   
  6.     # check each window      
  7.     for h_window in h_windows:          
  8.          # get child button with text OK, Yes or Confirm of given window  
  9.         h_btn = win32gui.FindWindowEx(h_window, None,'Button', None)  
  10.         if not h_btn: continue  
  11.         # check button text  
  12.         text = win32gui.GetWindowText(h_btn)  
  13.         if not text.lower() in ('ok', 'yes', 'confirm'): continue  
  14.         # click button  
  15.         win32gui.PostMessage(h_btn, win32con.WM_LBUTTONDOWN, None, None)  
  16.         time.sleep(0.2)  
  17.         win32gui.PostMessage(h_btn, win32con.WM_LBUTTONUP, None, None)  
  18.         time.sleep(0.2) 

最后,實例演示結束全文,以后再也不用擔心意外彈窗了。

[^1]: Handling VBA popup message boxes in Microsoft Excel

[^2]: Trying to catch MsgBox text and press button in xlwings

[^3]: What is pywinauto

[^4]: Remote Execution Guide 

 

責任編輯:龐桂玉 來源: Python中文社區(qū) (ID:python-china)
相關推薦

2023-10-18 13:57:17

2021-04-17 23:10:59

Python微軟Word

2022-03-21 10:09:08

PythonExcel郵件

2020-11-05 12:56:19

Python辦公自動化

2024-05-29 11:16:33

PythonExcel

2018-02-25 19:29:49

自動化數(shù)字化IT

2024-06-12 12:36:48

CrontabPython

2018-05-11 08:29:10

Python自動化測試數(shù)據(jù)驅動

2018-05-11 13:39:05

PythonCSV接口測試

2020-04-21 10:45:47

PythonWordExcel

2021-07-14 13:11:02

papermillJupyterPython

2010-07-26 09:53:08

Perl多進程

2010-07-14 11:14:48

Perl多進程

2022-12-13 10:41:27

2017-12-17 21:58:18

2021-08-17 09:00:00

架構PythonWeb

2024-05-21 09:52:19

2021-07-04 12:44:04

PythonExcel身份證

2020-12-08 08:00:00

機器學習人工智能超自動化

2020-12-04 19:08:57

自動化數(shù)字化機器人
點贊
收藏

51CTO技術棧公眾號