Python天天美味(36) – 用Python實現Spy++

來源:互聯網
上載者:User
文章目錄
  • Spy++原理
  • PyWin32對應的函數
  • 代碼實現
  • 示範

Spy++是微軟出品的用來擷取Window視窗資訊的一個小工具。實現的原理其實不難,通過調用某些特定的Windows API即可。於是,我打算用Python也實現一個功能簡化版本的小工具,取名叫PySpy++。Python中調用Windows API一般使用pywin32這套庫,介面庫我使用PyQT4。

Spy++原理

Spy++中,最常用的一個功能,就是識別視窗。其中主要需要用到的Windows API有:

擷取當前滑鼠位置

BOOL GetCursorPos( LPPOINT lpPoint );

擷取位於指定位置的視窗控制代碼

HWND WindowFromPoint( POINT Point );

擷取視窗類別別

int GetClassName( HWND hWnd, LPTSTR lpClassName, int nMaxCount );

擷取視窗內容或標題

方法一:

int GetWindowText( HWND hWnd, LPTSTR lpString, int nMaxCount );

這個API有時候不能取到某些控制項的值,因此,使用方法二。

方法二:

給視窗發送WM_GETTEXT訊息:

LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM  wParam, LPARAM lParam );

高亮選中的視窗

先擷取當前視窗的大小,然後畫一個矩形框。

BOOL GetWindowRect( HWND hWnd, LPRECT lpRect );

BOOL Rectangle(
    HDC hdc, // handle to DC
    int nLeftRect, // x-coord of upper-left corner of rectangle
    int nTopRect, // y-coord of upper-left corner of rectangle
    int nRightRect, // x-coord of lower-right corner of rectangle
    int nBottomRect // y-coord of lower-right corner of rectangle
);

滑鼠移開視窗後,視窗需要恢複原狀,需要重新重新整理:

BOOL InvalidateRect(
    HWND hWnd, // handle to window
    CONST RECT* lpRect, // rectangle coordinates
    BOOL bErase // erase state
);

BOOL UpdateWindow(
    HWND hWnd // handle to window
);

BOOL RedrawWindow(
    HWND hWnd, // handle to window
    CONST RECT *lprcUpdate, // update rectangle
    HRGN hrgnUpdate, // handle to update region
    UINT flags // array of redraw flags
);

PyWin32對應的函數

在Python中調用Windows API,首先下載PyWin32,地址:http://pywin32.sourceforge.net/

安裝完成後,開啟協助文檔Python for Windows Documentation,裡面有所有需要的東西,隨時用來查看。

常用的API在win32api模組裡,介面相關的API在win32gui模組裡,API參數中定義的一些常量在win32con模組中。上面的Windows API對應PyWin32中的函數為:

(int, int) = win32gui. GetCursorPos()
int = win32gui. WindowFromPoint(point)
string = win32gui. GetClassName(hwnd)
string = win32gui. GetWindowText(hwnd)
int = win32gui. SendMessage(hwnd, message , wparam , lparam )
(left, top, right, bottom) = win32gui. GetWindowRect(hwnd)
win32gui. Rectangle(hdc, LeftRect, TopRect, RightRect, BottomRect)
win32gui. InvalidateRect(hWnd, Rect, Erase)
win32gui. UpdateWindow(hwnd)
win32gui. RedrawWindow(hWnd, rcUpdate, hrgnUpdate, flags)

代碼實現

介面庫使用PyQT4,參考資料可以從我之前的一篇部落格裡瞭解:PyQt4 學習資料匯總

工具對話方塊視窗有兩個控制項,一個是QLabel控制項,一個是QTextEdit控制項。QLabel控制項就是那個用來滑鼠按下去後去捕捉視窗,QTextEdit控制項用來顯示視窗的資訊。為了讓QTextEdit響應自訂的滑鼠事件,我建立了一個自訂QLabel控制項SpyLabel,繼承自QLabel。

class SpyLabel(QtGui.QLabel):
    def __init__(self, parent = None):
        QtGui.QLabel.__init__(self, parent)
        self.parent = parent
        self.spying = False
        self.rectanglePen = win32gui.CreatePen(win32con.PS_SOLID, 3, win32api.RGB(255, 0, 0))
        self.prevWindow = None
        self. setCursor(QtCore.Qt.SizeAllCursor)

SpyLabel中處理滑鼠移動事件:

def mouseMoveEvent(self, event):
    if self.spying:
        curX, curY = win32gui.GetCursorPos()
        hwnd = win32gui. WindowFromPoint((curX, curY))

        if self.checkWindowValidity(hwnd):               
            if self.prevWindow:
                self.refreshWindow(self.prevWindow)
            self.prevWindow = hwnd
            self.highlightWindow(hwnd)
            self.displayWindowInformation(hwnd)

滑鼠鬆開事件:

def mouseReleaseEvent(self, event):
    if self.spying:
        if self.prevWindow:
            self.refreshWindow(self.prevWindow)
        win32gui.ReleaseCapture()
        self.spying = False

高亮視窗的函數:

def highlightWindow(self, hwnd):
    left, top, right, bottom = win32gui.GetWindowRect(hwnd)
    windowDc = win32gui.GetWindowDC(hwnd)
    if windowDc:
        prevPen = win32gui.SelectObject(windowDc, self.rectanglePen)
        prevBrush = win32gui.SelectObject(windowDc, win32gui.GetStockObject(win32con.HOLLOW_BRUSH))

        win32gui.Rectangle(windowDc, 0, 0, right - left, bottom - top)
        win32gui.SelectObject(windowDc, prevPen)
        win32gui.SelectObject(windowDc, prevBrush)
        win32gui.ReleaseDC(hwnd, windowDc)

重新整理視窗的函數:

def refreshWindow(self, hwnd):
    win32gui. InvalidateRect(hwnd, None, True)
    win32gui. UpdateWindow(hwnd)
    win32gui. RedrawWindow(hwnd, 
        None, 
        None,  
        win32con.RDW_FRAME|
            win32con.RDW_INVALIDATE|
            win32con.RDW_UPDATENOW|
            win32con.RDW_ALLCHILDREN)

顯示視窗資訊:

def displayWindowInformation(self, hwnd):
    className = win32gui.GetClassName(hwnd)
    buf_size = 1 + win32gui. SendMessage(hwnd, win32con.WM_GETTEXTLENGTH, 0, 0)
    buffer = win32gui.PyMakeBuffer(buf_size)
    win32gui. SendMessage(hwnd, win32con.WM_GETTEXT, buf_size, buffer)
    windowText = buffer[:buf_size]

    try:
        windowText = unicode(windowText, 'gbk')
    except:
        pass

    message = ['Handle:\t' + str(hwnd),
               'Class Name:\t' + className,
               'Window Text:\t' + windowText]
    self.output('\r\n'.join(message))

注意到上面SendMessage函數,需要傳入一個分配的緩衝區,用於擷取返回的內容。這裡使用了:

buffer = win32gui.PyMakeBuffer(buf_size)

由於返回的內容中可能有中文,因此使用unicode(windowText, 'gbk')進行一下轉換。

示範

 

二進位下載:

http://pyspyplusplus.googlecode.com/files/pyspy++.exe 

原始碼:

http://code.google.com/p/pyspyplusplus/ 

 

 

Python 天天美味系列(總)

Python 天天美味(31) - python資料結構與演算法之插入排序 

Python 天天美味(32) - python資料結構與演算法之堆排序 

Python 天天美味(33) - 五分鐘理解元類(Metaclasses)[轉]

Python 天天美味(34) - Decorators詳解

Python 天天美味(35) - 細品lambda 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.