基本思路 为什么要选择python制作Windows应用程序,主要就是源代码直接展示,发现问题随时修改,同时可以不断增加新的功能方便。 由于C语言的Windows程序中结构类型在python中不能使用, 因此我们按照ctypes模块指导意见继承structure为类来创建使用,但是我也使用python中的命名元组方式来使用C语言的结构类型,所以对于同样的C语言结构类型我们可以自己解释为python不同的类型 ,只要自己使用时知道如何使用C语言结构的各个域字段就可以。同时C语言在switch case语句只有python3.10及以后版本支持对应的语句 match case 所以注意python版本,安装支持库pywin32, 我的在Windows10下测试运行通过,我会给出实际代码,最后显示运行效果图 供初学者参考。
例如 C语言的结构
typedef struct tagRECT
{ LONG left;
LONG top;
LONG right;
LONG bottom; } RECT
在python中 我们定义为
LONG = ctypes.c_long class RECT(ctypes.Structure): _fields_ = [("left", LONG), ("top", LONG), ("right", LONG), ("bottom", LONG)]
我同样可以定义为命名元组 ,不过按官方建议最好按上面的定义。
但是我知道如何解释 所以我也涉及到元组定义,例如
ButtonTuple=collections.namedtuple("ButtonTuple","iStyle szText chinese")
该程序主要涉及几个文件,我通常文件命名后面加个数字1,其内容非常简单,以示区别,定义多个文件主要目的是让自己的程序看起来不太臃肿 。展示一种编程风格而已 一定要遵循这种原则、 以后自己的其他程序可以重复利用
1、主程序文件 main.py
2、涉及C语言结构 Windows类型定义的文件 structureType1.py
3、宏定义文件 microDefine1.py
4、按钮类型常数文件 ButtonTypeConst1.py
1、main.py 代码
import collections import ButtonTypeConst1 from structureType1 import * import ctypes from micro1 import * import win32con import win32gui import sys user32=ctypes.windll.user32 gdi32=ctypes.windll.gdi32 clib=ctypes.cdll.msvcrt printf=ctypes.windll.LoadLibrary("msvcrt.dll") #给元组元素起名称 然后可以使用名称来引用数据 #我能创建一个以ButtonTuple为名称的元组 就如C语言的数据结构一样 给每个域起个有意义的名字 而我们不必 #关心数据的类型 自己在初始化的时候自己检查 增加编程的灵活性 ButtonTuple=collections.namedtuple("ButtonTuple","iStyle szText chinese") buttonList=[] buttonList.append(ButtonTuple(ButtonTypeConst1.BS_PUSHBUTTON,"pushButton","普通按钮")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_DEFPUSHBUTTON,"DEFPUSHBUTTON","下压按钮")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_CHECKBOX,"CHECKBOX","复选框")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_AUTOCHECKBOX,"AUTOCHECKBOX","自动复选框")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_RADIOBUTTON,"RADIOBUTTON","自动选项按钮")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_3STATE,"3STATE","三态按钮")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_AUTO3STATE,"AUTO3STATE","自动三态按钮")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_GROUPBOX,"GROUPBOX","分组框")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_AUTORADIOBUTTON,"AUTORADIOBUTTON","自动选项按钮")) buttonList.append(ButtonTuple(ButtonTypeConst1.BS_OWNERDRAW,"OWNERDRAW","拥有者绘制按钮")) NUM=len(buttonList) szAppName="ButtonLock" hwndButtons=[] #C语言中数组 python对应列表 rect=RECT() szTop="message wParam lParam 中文" szUnd="_______ ______ ______ ______" szFormat="%-16s%04X-%04X %08X-%08x %s " szbuffer=ctypes.create_string_buffer(b"0", 150) cxChar=0 cyChar=0 ps=PAINTSTRUCT() msg = MSG() hInstance=win32gui.GetModuleHandle(None) def windowProc(hwnd, msg, wParam, lParam): # global hwndButton global cxChar global cyChar global NUM match msg: case win32con.WM_CREATE: #相当于初始化 cxChar=LOWORD(user32.GetDialogBaseUnits()) cyChar=HIWORD(user32.GetDialogBaseUnits()) for i in range(NUM): hwndButtons.append(win32gui.CreateWindow("button", #预定义类名 buttonList[i].szText, #按钮标题 win32con.WS_CHILD | win32con.WS_VISIBLE | buttonList[i].iStyle, #按钮风格 cxChar,cyChar*(1+2*i), #按钮坐标 20*cxChar,7*cyChar//4, #按钮宽 高 hwnd,i, #父窗口句柄 按钮ID hInstance,None)) #实例句柄 return 0 case win32con.WM_SIZE: rect.left = 24 * cxChar rect.top = 2 *cyChar rect.right=LOWORD(lParam) rect.bottom=HIWORD(lParam) return 0 case win32con.WM_PAINT: user32.InvalidateRect(hwnd,ctypes.byref(rect),True) hdc=user32.BeginPaint(hwnd,ctypes.byref(ps)) gdi32.SelectObject(hdc,gdi32.GetStockObject(win32con.SYSTEM_FIXED_FONT)) gdi32.SetBkMode(hdc,win32con.TRANSPARENT) gdi32.TextOutW(hdc,24*cxChar,cyChar,szTop,len(szTop)) gdi32.TextOutW(hdc,12*cyChar,cyChar,szUnd,len(szUnd)) user32.EndPaint(hwnd,ctypes.byref(ps)) return 0 case win32con.WM_DRAWITEM : pass case win32con.WM_COMMAND: user32.ScrollWindow(hwnd,0,-cyChar,ctypes.byref(rect),ctypes.byref(rect)) hdc=user32.GetDC(hwnd) gdi32.SelectObject(hdc,gdi32.GetStockObject(win32con.SYSTEM_FIXED_FONT)) #wparam高位字节为按钮风格 index=LOWORD(wParam) print("按钮的索引",index) gdi32.TextOutW(hdc,24*cxChar,cyChar*(rect.bottom//cyChar-1), szbuffer,user32.wsprintfW(szbuffer,szFormat,"WM_DRAWITEM" if msg==win32con.WM_DRAWITEM else "WM_COMMAND",HIWORD(wParam), LOWORD(wParam),HIWORD(lParam),LOWORD(lParam),buttonList[index].chinese)) user32.ReleaseDC(hwnd,hdc) user32.ValidateRect(hwnd,ctypes.byref(rect)) case win32con.WM_DESTROY: user32.PostQuitMessage(0) return 0 return win32gui.DefWindowProc(hwnd,msg,wParam,lParam) #---------第一步 初始化类结构-python版本和C语言版本有一定的不同 某些域没有cbClsExtra----------------------------------------- #---------窗口类的作用就是定义窗口的一般性特征 或者通用特征 wndClass = win32gui.WNDCLASS() wndClass.cbWndExtra=0 wndClass.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW | win32con.CS_DBLCLKS #每当窗口水平方向或垂直方向尺寸发生改变后 要完全刷新窗口 wndClass.lpfnWndProc = windowProc #这个过程要处理基于这个窗口类创建的所有窗口的全部消息 使用函数名 实际引用提供指向函数的指针 wndClass.hInstance = win32gui.GetModuleHandle(None) #程序的实例句柄 wndClass.hCursor = win32gui.LoadCursor(None, win32con.IDC_ARROW) #使用预定义图标 第一个参数为None 使用自定义图片 第一个参数为程序的实例句柄 wndClass.hbrBackground = win32con.COLOR_WINDOW #win32gui.GetStockObject(win32con.WHITE_BRUSH) 或者获取图像对象#将窗口客户区边界设置为指定颜色 wndClass.lpszClassName = szAppName #--------第二步 注册类--------------------------------------------- wndClassAtom = win32gui.RegisterClass(wndClass) #因为python中变量名就是结构体的地址 无须像C语言使用取地址运算符& if(wndClassAtom==0): print("注册失败") sys.exit(0) # print("注册结果",wndClassAtom) #-------第三步 创建程序主窗口------------------------------------------------- #窗口具有垂直和水平滚动条win32con.WS_HSCROLL=0x00100000 in32con.WS_VSCROLL=0x00200000L hwnd = win32gui.CreateWindow(szAppName, "我的窗口buttonlock", win32con.WS_OVERLAPPEDWINDOW | 0x00100000 |0x00200000, 100, 100, 500, 500, None, None, win32gui.GetModuleHandle(None), None) #---------第四步 显示并更新窗口 user32.ShowWindow(hwnd, win32con.SW_SHOW) #产生一个WM_SIZE消息 user32.UpdateWindow(hwnd) #产生一个WM_PAINT消息 #-------第五步 创建消息结构体并建立消息循环 ------------------------------- # msg = ctypes.wintypes.MSG() wParam=None lparam=None #手动调用一次回调函数 python调试时中不自动执行创建初始化 # windowProc(hwnd,win32con.WM_CREATE,0,0) #-------自己使用函数调用--------------------------- user32.SendMessageA(hwnd,win32con.WM_CREATE,wParam) hdc=user32.GetDC(hwnd) testString="测试文字\n又是一个" #注意 参数涉及字符串的函数可能都有ASCII版本和Unicode版本 gdi32.TextOutW(hdc,0,0,testString,len(testString)) user32.ReleaseDC(hwnd,hdc) #第六步 自动执行消息队列 msg由操作系统自动生成 传递给你的程序 while user32.GetMessageW(ctypes.byref(msg), None, wParam, lparam) != 0: user32.TranslateMessage(ctypes.byref(msg)) user32.DispatchMessageW(ctypes.byref(msg)) 2、 structureType1.py 代码
import ctypes HANDLE = ctypes.c_void_p HDC = HANDLE DWORD = ctypes.c_ulong HWND=ctypes.c_void_p LONG = ctypes.c_long BOOL = ctypes.c_long BYTE = ctypes.c_byte WCHAR = ctypes.c_wchar UINT = ctypes.c_uint INT=ctypes.c_int if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p): WPARAM = ctypes.c_ulong LPARAM = ctypes.c_long elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p): WPARAM = ctypes.c_ulonglong LPARAM = ctypes.c_longlong class RECT(ctypes.Structure): _fields_ = [("left", LONG), ("top", LONG), ("right", LONG), ("bottom", LONG)] tagRECT = _RECTL = RECTL = RECT class PAINTSTRUCT(ctypes.Structure): _fields_=[("hdc",HDC), ("fErase",BOOL), ("rcPaint",RECT), ("fRestore",BOOL), ("fIncUpdate",BOOL), ("rgbReserved",BYTE * 32)] tagPAINTSTRUCT=PAINTSTRUCT class TEXTMETRIC(ctypes.Structure): _fields_=[("tmHeight",LONG), ("tmAscent",LONG), ('tmDescent',LONG), ('tmInternalLeading', LONG), ('tmExternalLeading',LONG ), ('tmAveCharWidth',LONG ), ('tmMaxCharWidth',LONG ), ('tmWeight',LONG ), ('tmOverhang',LONG ), ('tmDigitizedAspectX',LONG ), ('tmDigitizedAspectY',LONG), ('tmFirstChar',WCHAR), ('tmLastChar',WCHAR), ('tmDefaultChar', WCHAR), ('tmBreakChar',WCHAR ), ('tmItalic',BYTE ), ('tmUnderlined',BYTE ), ('tmStruckOut', BYTE ), ('tmPitchAndFamily',BYTE), ('tmCharSet',BYTE)] TEXTMETRICW=PTEXTMETRICW=NPTEXTMETRICW=LPTEXTMETRICW=TEXTMETRIC class SCROLLINFO(ctypes.Structure): _fields_=[('cbSize',UINT), #set to sizeof(SCROLLINFO) ('fMask',UINT), #value to set or get ('nMin',INT), #minimum range value ('nMax',INT), #maximum range value ('nPage',UINT), #page size ('nPos',INT), #current position ('nTrackPos',INT) #current track position ] tagSCROLLINFO=PSCROLLINFO=SCROLLINFO class WNDPROC(ctypes.Structure): _fields_=[("unnamedParam1",HWND), ("namedParam2",UINT), ("unnamedParam3",WPARAM), ("unnamedParam4",LPARAM)] HINSTANCE=HANDLE HICON=HANDLE HCURSOR=HANDLE HBRUSH=HANDLE LPCWSTR=HANDLE class WNDCLASS(ctypes.Structure): _fields_=[("style",UINT), ("lpfnWndProc",WNDPROC), ("cbClsExtra",ctypes.c_int), ("cbWndExtra",ctypes.c_int), ("hInstance",HINSTANCE), ("hIcon",HICON), ("hCursor",HCURSOR), ('hbrBackground',HBRUSH), ('lpszMenuName',LPCWSTR), ('lpszClassName',LPCWSTR)] class POINT(ctypes.Structure): _fields_ = [("x", LONG), ("y", LONG)] tagPOINT = _POINTL = POINTL = POINT #演示 验证初始化后是否正确打印每个域的值 p=PAINTSTRUCT(1111,1) class MSG(ctypes.Structure): _fields_ = [("hWnd", HWND), ("message", UINT), ("wParam", WPARAM), ("lParam", LPARAM), ("time", DWORD), ("pt", POINT)] tagMSG = MSG COLORREF=ctypes.c_uint32 class LOGPEN(ctypes.Structure): _fields_=[('lopnStyle',UINT), ('lopnWidth',POINT), ('lopnColor',COLORREF)] PLOGPEN=NPLOGPEN=LPLOGPEN=LOGPEN print(p.hdc,p.fErase) print(ctypes.sizeof(SCROLLINFO)) class pythonArrayD2(): def __init__(self,rows,columns): assert isinstance(rows,int),"参数必须是正整数" assert isinstance(rows, int), "参数必须是正整数" self.rows=rows self.columns=columns self.Values={} self.Values=self.initValues() def initValues(self): for i in range(self.rows): for j in range(self.columns): self.Values[str(i)+','+str(j)]=0 return self.Values
3、microDefine1.py 代码
def LOWORD(x): return x & 0xffff def HIWORD(x): return (x>>16) & 0xffff def MAX(a,b): if a>b: return a else: return b def MIN(a,b): if a<b: return a else: return b def RGB(red,green,blue): assert isinstance(red,(int,float)),"必须输入整数" assert isinstance(green, (int, float)), "必须输入整数" assert isinstance(green, (int, float)), "必须输入整数" red=int(red) green=int(green) blue=int(blue) if red>255: red=min(red,255) if red<0: red=max(0,red) if green>255: green=min(green,255) if green<0: green=max(0,green) if blue>255: blue=min(blue,255) if blue<0: blue=max(0,blue) return blue*(1<<16)+green*(1<<8)+red
4、ButtonTypeConst1.py 代码
BS_PUSHBUTTON=0 BS_DEFPUSHBUTTON=1 BS_CHECKBOX=2 BS_AUTOCHECKBOX=3 BS_RADIOBUTTON=4 BS_3STATE=5 BS_AUTO3STATE=6 BS_GROUPBOX=7 BS_AUTORADIOBUTTON=8 BS_OWNERDRAW=9
运行效果