2017年4月7日 星期五

Python環境下最單純的GUI產生法~使用ctypes調用Windows API~

Python用了一陣子
很多特性確實令人讚賞
除了要把寫好的程式給其它人使用...
一般來說,通常都是使用py2exe或pyinstaller之類的輔助程式
將寫好的python檔,轉換與打包成可獨立執行的檔案
這樣做的唯一小缺點就是當python大改版時
還要等原作者更新,才有辦法使用,像py2exe目前只支援3.4版
超過就只能靠pyinstaller,但最新的3.6它也不支援
雖然以我的用法來說,應該沒有非3.6不可
但萬一遇到了呢?
似乎用Embedded版,然後用批次檔的方式達執行還可以
只是一般人都用視窗介面習慣過,Embedded不會包TKinter
又是個難關......

所以,捨棄上面的作法的最理想的目標就是...
看能不能有單純搭配官方的Embedded版就可以跑GUI的方案
就這樣東找西查之下,看到了一個用python跑windows api的範例
https://gist.github.com/mouseroot/6128651
複製程式碼後,執行下去。喔喔,有問題...
然後看了一下底下的討論,有一個地方有問題,再改一下...
喔喔,可以跑耶!
只靠embedded的最單純環境下
寫一個python xxx.py的批次檔就有視窗GUI可用
這個就是我想要的
然後就接著研究這個ctypes模組怎麼跑Windows API


其實這個ctypes的主要用途其實是直接使用c/c++開發出來的動態函式庫的
也就是在Windows底下的DLL或Linux底下的SO檔
只要你知道裡面有哪些東西可以用(物件啦,函數啦)
可以直接從python呼叫來用...哇靠,太強大了
所以原本就存在user32.dll與gdi32.dll的Windows API就可以使用
頂多稍稍處理一下就行............
好啦,其實還是要處理不少部分
沒碰過傳統Windows API的人大概就先投降轉頭用Tkinter

首先要處理的就是定義Windows Prodecure函數定義

WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM)

再來一堆定義在WinUser.h裡的參數數值
例如,WM_CREATE其實是1、WM_DESTROY其實是2之類的
因為東西實在太多了,在網路上找到不少範例都額外寫成另一個模組引入使用
CW_USEDEFAULT = 0x80000000
WM_DESTROY = 2
WHITE_BRUSH = 0
接著就是WNDCLASSEX這種Windows API專用Structure要自已刻出來
其它如果用到RECT、PIONT與BITMAP之類也是要仿照格式刻出來
class WNDCLASSEX(Structure):
    _fields_ = [("cbSize", c_uint),
                ("style", c_uint),
                ("lpfnWndProc", WNDPROCTYPE),
                ("cbClsExtra", c_int),
                ("cbWndExtra", c_int),
                ("hInstance", HANDLE),
                ("hIcon", HANDLE),
                ("hCursor", HANDLE),
                ("hBrush", HANDLE),
                ("lpszMenuName", LPCWSTR),
                ("lpszClassName", LPCWSTR),
                ("hIconSm", HANDLE)]
最後才是依Windows Prodecure函數自定義
建立main主函數,函數內建立主視窗,傳遞Message物件
到實際執行main,去跑之前的設定

不過,用python以Windows API直接產生視窗介面的作法
唯一的好處大概只有壓低小程式上執行檔的容量而已
同樣用py2exe或pyinstaller打包一個單純的視窗範例
不包含TK模組可以少掉10MB左右的空間
但是要使用Windows API到可以做到跟TK一樣多的事......
這要花的工夫可不少,而且產生出來的東西只能在Windows環境下跑
本來用python的目的之一是要簡便,搞這種事有點畫蛇添足之感
嗯...............
反正當初學視窗已經熟悉基本Windows API用法
就當多學個方法吧

額外再補充一下,此範例用64位元的Python跑會有問題
要用32位元的Python才會正常,推論是ctype的預設載入是32位元的視窗元件
要正常跑64位元的,需要的工作應該不少,還是暫時跑32位元就好

沒有留言: