python辦公自動化之圖像對比&模擬鼠標點擊

  小爬之前的一系列博文中,提到過基於Pywin32、Uiautomation、SAP Scripting API、Office macro等手段來實現windows桌面應用的自動化。但是,凡事總有例外。有些應用程序使用的一些自定義、自繪製的控件,這類控件無論是使用SPY++還是inspect之類軟件都很難成功捕獲。主流的RPA廠商,面對這類控件,也只能通過識別某個控件對應的圖像在窗口中的位置,再來模擬鍵盤鼠標實現點擊或者輸入。如果我們希望用python自己編寫代碼來實現這一功能,又需要怎麼做呢?小爬決定帶大家來一探究竟。

  小爬就以 金蝶K3的某桌面客戶端舉例,通過下圖不難看出,Microsoft Spy++拿它無可奈何,只能獲取到主窗口句柄,而子窗口完全拿不到,我們如何通過代碼實現自動點擊下圖中【程序列表】處的【金蝶K3 WISE】呢?你們不妨先思考下,我這裏短暫賣個關子先。

  

 

   下面全是滿滿的乾貨,看完保證您直呼So Easy!具體思路如下:

①、對上圖中的【金蝶 K3 WISE】圖標區域截圖,存儲在本地目錄,比如就放置於我們腳本的同級目錄,姑且命名爲【obj.png】:

②利用pywin32中的win32gui函數,也就是FindWindow來獲得該客戶端主窗口句柄,進而拿到其在系統桌面中的位置和寬高;

③、利用圖形庫Pillow中的imageGrab.grab方法,對桌面的某個區域,也就是該客戶端主窗口區域截圖,作爲我們的源圖,保存在內存中,可以不用存儲爲本地圖片文件;

④、利用aircv的圖像對比方法,來實現根據源圖定位目標圖的位置中心點;

⑤、根據步驟④得到的目標中心點相對座標,計算得到該中心點在整個桌面的絕對位置座標;

⑥、利用win32api中的SetCursorPos、mouse_event等方法來驅動光標位置,模擬鼠標點擊等,最終實現自動化。

  其實上面的步驟還可以簡化,我們的源圖可以直接是我們的桌面截屏,而無需獲取應用程序的主窗口位置,那樣拿到的就已經是目標圖片在桌面的絕對位置,本文中暫且先按照上面的六個步驟來實現。

其中利用步驟④拿到圖片在窗口中的位置後,可以藉助opencv來顯示出來,代碼如下:

 1 import cv2
 2 import aircv as ac
 3 import numpy as np
 4 from PIL import ImageGrab,Image
 5 import win32api,win32con,win32gui
 6 def draw_circle(img, center, radius, color,thickness):
 7     cv2.circle(img, center, radius, color,thickness)
 8     cv2.imshow('objDetect', imsrc) 
 9     cv2.waitKey(0)
10     cv2.destroyAllWindows()
11 
12 if __name__ == "__main__":
13     '''通過spy++拿到應用程序主窗口的類名和窗口標題'''
14     mainHnd=win32gui.FindWindow("MainFrameWnd","異速聯客戶端 7.0.0.0")
15 
16     '''根據GetWindowRect拿到主窗口的左頂點的位置座標(x,y)和窗口的寬高(w*h)'''
17     rect = win32gui.GetWindowRect(mainHnd)
18     x,y=rect[0],rect[1]
19     w,h=rect[2] - x,rect[3] - y
20     img=ImageGrab.grab(bbox=rect) # (left_x, top_y, right_x, bottom_y)
21     # img.save("src.png") # 可以截圖保存測試下,是否截圖位置準確,非必須步驟
22     imsrc=np.array(img)
23 
24     r, g, b = cv2.split(imsrc)
25     cv2.merge([b,g,r], imsrc) # CV中是以BGR的順序來表達一個順序,與傳統的RGB標準不一致,所以需要重組ndArray
26     imobj = ac.imread('obj.png')
27     pos = ac.find_template(im_source=imsrc,im_search=imobj,threshold=0.8)
28     circle_center_pos = (int(pos['result'][0]),int(pos['result'][1]))
29     circle_radius = 50
30     color = (255, 0, 0)
31     line_width = 2
32     draw_circle(imsrc, circle_center_pos, circle_radius, color,line_width)

運行該程序後,opencv會幫我們繪製出目標圖像在窗口中的位置,效果如下圖所示:

  之後就是模擬鍵盤和鼠標:移動光標到桌面指定位置,點擊該區域了,需要注意的是,如果要這塊功能生效,需要提前將pycharm或者 visual studio code以管理員權限打開,否則可能看不到實際效果。整個功能完整的代碼如下:

 1 import cv2
 2 import aircv as ac
 3 import numpy as np
 4 from PIL import ImageGrab,Image
 5 import win32api,win32con,win32gui
 6 def draw_circle(img, center, radius, color,thickness):
 7     cv2.circle(img, center, radius, color,thickness)
 8     cv2.imshow('objDetect', imsrc) 
 9     cv2.waitKey(0)
10     cv2.destroyAllWindows()
11 
12 if __name__ == "__main__":
13     '''通過spy++拿到應用程序主窗口的類名和窗口標題'''
14     mainHnd=win32gui.FindWindow("MainFrameWnd","異速聯客戶端 7.0.0.0")
15 
16     '''根據GetWindowRect拿到主窗口的左頂點的位置座標(x,y)和窗口的寬高(w*h)'''
17     rect = win32gui.GetWindowRect(mainHnd)
18     x,y=rect[0],rect[1]
19     w,h=rect[2] - x,rect[3] - y
20     img=ImageGrab.grab(bbox=rect) # (left_x, top_y, right_x, bottom_y)
21     # img.save("src.png") # 可以截圖保存測試下,是否截圖位置準確,非必須步驟
22     imsrc=np.array(img)
23 
24     r, g, b = cv2.split(imsrc)
25     cv2.merge([b,g,r], imsrc) # CV中是以BGR的順序來表達一個順序,與傳統的RGB標準不一致,所以需要重組ndArray
26     imobj = ac.imread('obj.png')
27     pos = ac.find_template(im_source=imsrc,im_search=imobj,threshold=0.8)
28     circle_center_pos = (int(pos['result'][0])+x,int(pos['result'][1])+y) # 計算對象圖片中心點 在桌面的絕對座標
29     win32api.SetCursorPos((circle_center_pos[0],circle_center_pos[1])) # 設置光標位置
30     win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) # 模擬鼠標左鍵按下
31     win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) # 模擬鼠標左鍵彈起

  想必屏幕前的童鞋已經迫不及待了,那就趕緊舉一反三,從實際業務中找到這類需求,麻溜地動手試試吧!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章