成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

用Python子進(jìn)程關(guān)閉Excel自動(dòng)化中的彈窗

開(kāi)發(fā) 后端 自動(dòng)化
利用Python進(jìn)行Excel自動(dòng)化操作的過(guò)程中,尤其是涉及VBA時(shí),可能遇到消息框/彈窗(MsgBox)。

[[397615]]

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

假想場(chǎng)景

使用xlwings(或者其他自動(dòng)化庫(kù))打開(kāi)Excel文件test.xlsm,讀取Sheet1!A1單元格內(nèi)容。很簡(jiǎn)單的一個(gè)操作: 

  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() 

然而不幸的是,打開(kāi)工作簿時(shí)進(jìn)行了熱情的歡迎儀式: 

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

第一個(gè)彈窗Welcome就卡住了Excel,Python代碼相應(yīng)卡死在第一行。

基本思路

主程序中不可能直接處理或者繞過(guò)此類問(wèn)題,也不能奢望有人隨時(shí)蹲守點(diǎn)擊下一步——那就開(kāi)啟一個(gè)子線程來(lái)護(hù)航吧。因此,解決方案是利用子線程監(jiān)聽(tīng)并隨時(shí)關(guān)閉彈窗,直到主程序圓滿結(jié)束。

解決這個(gè)問(wèn)題,需要以下兩個(gè)知識(shí)點(diǎn)(基礎(chǔ)知識(shí)請(qǐng)課外學(xué)習(xí)):

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

pywinauto方案

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

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

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

  •  title 需要捕捉的彈窗的標(biāo)題,例如Excel默認(rèn)彈窗的標(biāo)題為Microsoft Excel
  •  interval 監(jiān)聽(tīng)的頻率,即每隔多少秒檢查一次 
  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() 

于是,整個(gè)過(guò)程分為三步:

  •  啟動(dòng)子線程監(jiān)聽(tīng)彈窗
  •  主線程中打開(kāi)Excel開(kāi)始自動(dòng)化操作
  •  關(guān)閉子線程 
  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() 

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

win32gui方案

那就只好轉(zhuǎn)向相對(duì)底層的win32gui,所幸完美解決了上述問(wèn)題。

win32gui是pywin32庫(kù)的一部分,所以實(shí)際安裝命令是: 

  1. pip install pywin32 

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

  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) 

更一般的方案

更一般地,當(dāng)同時(shí)存在默認(rèn)標(biāo)題和自定義標(biāo)題的彈窗時(shí),就不便于采用標(biāo)題方式進(jìn)行捕捉了。例如 

  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" 

那就擴(kuò)大搜索范圍,依次點(diǎn)擊所有包含確定性描述的按鈕(例如OK,Yes,Confirm)來(lái)關(guān)閉彈窗。同理替換MsgBoxListener類的_close_msgbox()方法(同時(shí)構(gòu)造函數(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) 

最后,實(shí)例演示結(jié)束全文,以后再也不用擔(dān)心意外彈窗了。

[^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 

 

責(zé)任編輯:龐桂玉 來(lái)源: Python中文社區(qū) (ID:python-china)
相關(guān)推薦

2023-10-18 13:57:17

2021-04-17 23:10:59

Python微軟Word

2018-02-25 19:29:49

自動(dòng)化數(shù)字化IT

2022-03-21 10:09:08

PythonExcel郵件

2020-11-05 12:56:19

Python辦公自動(dòng)化

2024-06-12 12:36:48

CrontabPython庫(kù)

2024-05-29 11:16:33

PythonExcel

2018-05-11 13:39:05

PythonCSV接口測(cè)試

2020-04-21 10:45:47

PythonWordExcel

2018-05-11 08:29:10

Python自動(dòng)化測(cè)試數(shù)據(jù)驅(qū)動(dòng)

2021-07-14 13:11:02

papermillJupyterPython

2010-07-26 09:53:08

Perl多進(jìn)程

2010-07-14 11:14:48

Perl多進(jìn)程

2017-12-17 21:58:18

2022-12-13 10:41:27

2021-08-17 09:00:00

架構(gòu)PythonWeb

2024-05-21 09:52:19

2020-12-04 19:08:57

自動(dòng)化數(shù)字化機(jī)器人

2020-12-08 08:00:00

機(jī)器學(xué)習(xí)人工智能超自動(dòng)化

2021-07-04 12:44:04

PythonExcel身份證
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品视频免费观看 | 国产不卡在线播放 | 激情欧美一区二区三区 | 欧美日韩一区不卡 | 欧美激情一区二区三区 | 四虎首页 | 日韩在线国产 | 97伊人| 国产美女自拍视频 | 欧美亚洲视频在线观看 | 亚洲精品成人 | 国产无套一区二区三区久久 | 婷婷开心激情综合五月天 | 91精品国产91久久久久久密臀 | 91在线免费视频 | 久久久久久久久久久一区二区 | 欧美日韩亚洲一区 | 一区二区三区韩国 | 成人av播放 | 午夜精品91| 国产不卡一区 | 亚洲一区二区在线播放 | 亚洲五码在线 | 天堂久久天堂综合色 | 欧美性高潮 | 精品一区二区三区在线观看 | 岛国毛片在线观看 | 一区二区三区日韩 | 国产精品久久久久无码av | 午夜一区二区三区视频 | 欧美啪啪 | 亚洲国产精品一区二区第一页 | 欧美大片一区 | 欧洲一级毛片 | 亚洲一区二区三区视频在线 | 国产精品国产a级 | 欧美视频1区 | 国产精品99视频 | 国产精品久久久久久久久久妞妞 | 国产视频精品在线观看 | 久久久久国产 |