有天想說要用python來追蹤某視窗的畫面,並且使用圖像辨識opencv來辨識狀態,如果異狀則播放音效提醒。
該文作者最後仍未完成,因為功能也不是如想像的那般,可行是可行,不過侷限性太大了。作者也自行提出這個問題,想要找到解決方法。
關於如何對特定程式窗口截圖,首先要先瞭解到底截的對象是甚麼?
”’
import win32con # 各種win32函式的參數
import win32gui
import win32ui
import numpy as np
hwnd = the handle of a window
x = the x coordinate of the window
y = the y coordinate of the window
width = the width of the window
height = the height of the window
”’
# 創造輸出圖層
hwindc = win32gui.GetWindowDC(hwnd)
srcdc = win32ui.CreateDCFromHandle(hwindc)
memdc = srcdc.CreateCompatibleDC()
bmp = win32ui.CreateBitmap()
# 如果視窗最小化,則移到Z軸最下方
win32gui.SetWindowPos(hwnd, win32con.HWND_BOTTOM, x, y, width, height, win32con.SWP_NOACTIVATE)
# 複製目標圖層,貼上到 bmp
bmp.CreateCompatibleBitmap(srcdc, width, height)
memdc.SelectObject(bmp)
memdc.BitBlt((0 , 0), (width, height), srcdc, (0, 0), win32con.SRCCOPY)
# 將 bitmap 轉換成 np
signedIntsArray = bmp.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype=’uint8′)
img.shape = (height,width,4)
# 釋放device content
srcdc.DeleteDC()
memdc.DeleteDC()
win32gui.ReleaseDC(hwnd, hwindc)
win32gui.DeleteObject(bmp.GetHandle())
主要功能只是抓取特定窗口,但是在「最小化」的時候,卻只會抓到 icon。如果窗口不解除最小化,就沒有辦法抓到完整畫面。
依照JN主要參考的文法,它的做法是將圖層透明化、取消最大小化的動畫,最後再把窗口秀出來。因為透明化的窗口圖層不會被看見,就能有「偷偷地截取」的感覺,取消動畫是讓作動加速,而窗口秀出來是避免讓截圖只抓到icon。
到這裡還沒結束,如果我們在程式運作中,又想點開特定程式窗口,如果此時沒有將該圖層透明化給還原,那麼就會發現用滑鼠再怎麼點icon都沒有畫面。但是透明度一還原,就會發現其實程式圖層在上方,擋到其他程式窗口。按照主要參考文章,它的作法會將窗口最小化後,再把圖層透明度還原。但是,依照這樣的概念運作,就會發現工作列實在閃閃發亮很熱鬧,並沒有偷偷地截圖。
所以JN這裡採取將窗口圖層移動到最下方,再把透明度還原,而沒有把窗口最小化。
# 將 hwnd 的型態變成 WindowLong
s = win32gui.GetWindowLong(hwnd,win32con.GWL_EXSTYLE)
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, s|win32con.WS_EX_LAYERED)
# 將窗口最大小化動畫給取消
win32gui.SystemParametersInfo(win32con.SPI_SETANIMATION, 0)
# 如果沒有把 hwnd 的型態轉換,代入此函式會報錯
# 第一個 0 是 顏色,第二個 0 是 不透明度
”’
此部分程式碼,「不包含」還原圖層透明度。
”’
透過這樣的方式抓取特定窗口截圖,就能解決前提連結的問題,不僅不會被別的窗口蓋過,就連最小化的窗口也能抓取。此外,也改進主要文章的缺陷,JN的方式不會讓工作列不停閃爍。
這也是JN第一篇發在GitHub的程式碼,當了初心者那麼久了,總算有東西值得發布了。
2018.06.20 刪除了冗碼,並以win10校正,避免截圖抓到四周的透明部分。
後記
程式碼發布是JN第一次嘗試,看到別人的網站爾偶會有制式的表格,JN很好奇是怎麼樣產生的。如果有甚麼方法能夠快速產生展示程式碼的表格,也請拜託在下方留言教學。
現在寫程式能寫出以前沒有能力完成的,我想這就是經驗的累積吧。慢慢累積各種方法與技巧,融會貫通就又開啟了一扇門˙,而且是以前認為沒辦法寫出來的東西。
這篇文章也是讓JN第一次接觸win32系列,win32不只存在於python,在C#還是其他的程式語言都有。我想win32系列就是windows的架構,每天從螢幕看到的、鍵盤操作的,都能用win32系列來完成,而且能做到的更強大,但是,卻也更源頭的物件。這次還碰到二進位,之前在寫都沒有注意到位元,也花了一些時間學習如何處理。
最後還有一些詞彙釐清。會用「窗口」是因為這是中國用語,而台灣常講「視窗」,然而用窗口是比較容易找到教學。此次撰寫,也讓JN分清楚「window」、「handle」、「device content」、「layer」的差異,不同的詞都有不同呈現的方式,要瞭解自己操作的是哪一種,才不會混淆或作不出來。