1. 程式人生 > >如何利用Python和win32程式設計避免重複性體力勞動(一)——開始、FindWindow和FindWindowEx

如何利用Python和win32程式設計避免重複性體力勞動(一)——開始、FindWindow和FindWindowEx

請移步至這裡以獲得更加的排版和閱讀體驗,謝謝您

本系列文章假設各位看官對python是足夠熟悉的,但卻不太瞭解win32程式設計。

嘛。。其實我也沒學過win32程式設計,臉請各位看官隨意招呼。

需求:最近因為做課題,要把800個FaceGen軟體生成的三維面孔儲存成圖片,以後不排除每一張面孔還要生成某個特質上連續變化的圖片。FaceGen以抽取面孔的特徵向量來構建面孔,所以儲存的檔案相當精簡,只需要300位元組就能無損儲存面孔的全部資訊。一般的三維工具包是沒有辦法解析FaceGen軟體的fg格式的,而他們自己的SDK又死貴死貴的,3K刀貌似。。嘛,自己動手,豐衣足食。

分析:要做的事情其實很簡單,fg檔案已經都準備好,我只需要用FaceGen開啟它的fg檔案,然後直接Save to Image就好了。鑑於一系列原因,不是很想用按鍵精靈之類的東西。何況利用pywin32可以很方便的利用win32的一些介面,何必再去模擬操作。

Part 0: 開始之前?

首先,來這裡安裝一個Pywin32吧,Pywin32是一個Python庫,為python提供訪問Windows API的擴充套件,提供了齊全的windows常量、介面、執行緒以及COM機制等等。安裝後自帶一個pythonwin的IDE。或者也可以來這邊下載一個ActivePython,整合了pywin32和一些其他的庫以及一大堆的支援文件,他們的文件查起來是非常方便的。當然了,WIN32的一些相關函式在MSDN上也能直接找到。

其次,為了方面查詢目標視窗的控制代碼,可以下載一個微軟自家的Spy++,這玩意兒滿大街都是。有了它,還能很方便的檢視窗體的訊息。

控制代碼

是一個32位整數,在windows中標記物件用,類似一個dict中的key,詳情參看這篇文章

訊息是windows應用的重要部分,用來告訴窗體“發生了什麼”,比如給一個按鈕傳送BN_CLICKED這麼個訊息,按鈕就知道“哦,我被點了”,才能執行相應的下一步操作。本文將大量使用訊息機制。詳情參看這篇文章

Part 1: 查詢窗體控制代碼

貌似在win32程式設計的世界裡,包括視窗到文字框的所有控制元件就是窗體,所有的窗體都有獨立的控制代碼。要操作任意一個窗體,你都需要找到這個窗體的控制代碼,這裡,我們就可以用到FindWindow函式和FindWindowEx函式。在pywin32中,他們都屬於win32gui的模組。

  • FindWindow(lpClassName=None, lpWindowName=None):
    • 描述:自頂層視窗(也就是桌面)開始搜尋條件匹配的窗體,並返回這個窗體的控制代碼。不搜尋子視窗、不區分大小寫。找不到就返回0
    • 引數:
      • lpClassName:字元型,是窗體的類名,這個可以在Spy++裡找到。
      • lpWindowName:字元型,是視窗名,也就是標題欄上你能看見的那個標題。
    • 說明:這個函式我們僅能用來找主視窗。
  • FindWindowEx(hwndParent=0, hwndChildAfter=0, lpszClass=None, lpszWindow=None);
    • 描述:搜尋類名和窗體名匹配的窗體,並返回這個窗體的控制代碼。不區分大小寫,找不到就返回0。
    • 引數:
      • hwndParent:若不為0,則搜尋控制代碼為hwndParent窗體的子窗體。
      • hwndChildAfter:若不為0,則按照z-index的順序從hwndChildAfter向後開始搜尋子窗體,否則從第一個子窗體開始搜尋。
      • lpClassName:字元型,是窗體的類名,這個可以在Spy++裡找到。
      • lpWindowName:字元型,是視窗名,也就是標題欄上你能看見的那個標題。
    • 說明:找到了主視窗以後就靠它來定位子窗體啦。

有了這兩個函式,我們就可以寫出可以定義到任意一個窗體控制代碼的函式啦:

def find_idxSubHandle(pHandle, winClass, index=0):
    """
    已知子視窗的窗體類名
    尋找第index號個同類型的兄弟視窗
    """
    assert type(index) == int and index >= 0
    handle = win32gui.FindWindowEx(pHandle, 0, winClass, None)
    while index > 0:
        handle = win32gui.FindWindowEx(pHandle, handle, winClass, None)
        index -= 1
    return handle


def find_subHandle(pHandle, winClassList):
    """
    遞迴尋找子視窗的控制代碼
    pHandle是祖父視窗的控制代碼
    winClassList是各個子視窗的class列表,父輩的list-index小於子輩
    """
    assert type(winClassList) == list
    if len(winClassList) == 1:
        return find_idxSubHandle(pHandle, winClassList[0][0], winClassList[0][1])
    else:
        pHandle = find_idxSubHandle(pHandle, winClassList[0][0], winClassList[0][1])
        return find_subHandle(pHandle, winClassList[1:])

這樣在後續的呼叫中,我們就能使用我們定義的finde_subHandle來方便地找到某個特定的Edit窗體控制元件。

比如我們定義一個FaceGenWindow的類:

class FaceGenWindow(object):
    def __init__(self, fgFilePath=None):
        self.Mhandle = win32gui.FindWindow("FaceGenMainWinClass", None)
        print "FaceGen initialization compeleted"

實體化以後就可以很方便地在類的方法中呼叫find_subHandle函式來找到FaceGenWindow的子窗體了,比如某個Edit控制元件:

handle = find_subHandle(self.Mhandle, [("ComboBoxEx32", 1), ("ComboBox", 0), ("Edit", 0)])

另外,python中找回來的控制代碼都是十進位制整型,Spy++裡顯示的都是十六進位制整型,這個要注意下,除錯的時候用十六進位制輸出控制代碼,如下:

print "%x" % (handle)