这次主要演示C语言结构类型在python中如何使用 因为涉及知识点多 所以可以选择性查看部分代码 部分用到的外部文件,都在第一版中,这里不再重复,同时演示最终效果图 供初学者参考
import datetime
import sys
import structure1
from winsound import PlaySound
import winsound
import win32gui
import win32con
import ctypes
import cv2
import win32api
import ctypes.wintypes
import paintProc
import math
#引入systemMetrics函数数据
import systemMetricsDic1
import time
user32 = ctypes.windll.user32
gdi32=ctypes.windll.gdi32
import Globalvariable1
winmm=ctypes.windll.LoadLibrary(r"./winmm.dll")
debug=True
SND_FILENAME=0x20000
SND_ASYNC=0x01
#--------因为python是解释型语言 所以回调函数最先创建----------------------------------------
# cv2.namedWindow("img",cv2.WINDOW_NORMAL) #cv2.WINDOW_NORMAL窗口大小可变
# cvHwnd= win32gui.FindWindow(None, "img")
#
# print(cvHwnd)
keyDisplay=True
closeDisplay=False
crColor=0
pStringBuffer= ctypes.create_string_buffer(b"Hello",Globalvariable1.stringBufferSize)
def LOWORD(x):
return x & 0xffff
def HOWORD(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 windowProc(hwnd, msg, wParam, lParam):
si=structure1.SCROLLINFO()
ps=structure1.PAINTSTRUCT()
iPaintBeg=0
iPaintEnd=0
match msg:
case win32con.WM_CREATE:
# region Description 初始化事件
if debug:
print("窗口第一次创建\n")
PlaySound("./hello.wav",winsound.SND_ASYNC)
hdc = user32.GetDC(hwnd)
crColor=gdi32.GetPixel(hdc,15,15)
print("位置(15,15)处的颜色值为:",crColor)
r,g,b=Globalvariable1.getRGB(crColor)
print("位置(15,15)处的颜色值为:", r,g,b)
#------------自定义的结构类型--------------------
tm = structure1.TEXTMETRIC()
#采用引用类型 以便能修改传入的参数
gdi32.GetTextMetricsA(hdc, ctypes.byref(tm))
Globalvariable1.global_cxChar = tm.tmAveCharWidth
Globalvariable1.global_cyChar = tm.tmHeight + tm.tmExternalLeading
#tmPitchAndFamily低位为1 变宽字体 否则为等宽字体
if tm.tmPitchAndFamily & 0x1:
Globalvariable1.global_cxCaps = 1.5*Globalvariable1.global_cxChar
else:
Globalvariable1.global_cxCaps = 1 * Globalvariable1.global_cxChar
user32.ReleaseDC(hwnd, hdc)
cWidth=Globalvariable1.global_cxChar
Globalvariable1.global_int_VerticalScrollMaxPosition=40*cWidth + 44*Globalvariable1.global_cxCaps
# endregion
return 0
case win32con.WM_SIZE:
# region Description 处理窗口改变尺寸事件
Globalvariable1.global_cyClient = HOWORD(lParam)
Globalvariable1.global_cxClient = LOWORD(lParam)
# lParam 低位字为客户区宽度 高位字为客户区高度 通常行数就为lines=cyClient/cyChar colomuns=cxClient/cxChar
#为滚动条结构信息赋初值 设置垂直滚动条
si.cbSize=ctypes.sizeof(si)
si.fMask=win32con.SIF_RANGE | win32con.SIF_PAGE
si.nMin=0
si.nMax=Globalvariable1.global_int_displayHaveMaxLinesInClient
if Globalvariable1.global_cyChar!=0:
si.nPage=Globalvariable1.global_cyClient//Globalvariable1.global_cyChar
user32.SetScrollInfo(hwnd,win32con.SB_VERT,ctypes.byref(si),True)
#设置水平滚动条
si.cbSize = ctypes.sizeof(si)
si.fMask = win32con.SIF_RANGE | win32con.SIF_PAGE
si.nMin = 0
if Globalvariable1.global_cxChar!=0:
si.nMax = 2+(Globalvariable1.global_int_HorizontalScrollMaxPosition//Globalvariable1.global_cxChar)
if Globalvariable1.global_cxChar != 0:
si.nPage = Globalvariable1.global_cxClient // Globalvariable1.global_cxChar
user32.SetScrollInfo(hwnd, win32con.SB_HORZ, ctypes.byref(si), True)
if debug:
print("如果是第一个WM_SIZE消息,通常由调用ShowWindow函数产生")
print("你改变了窗口尺寸,消息编号是:", msg, " ", hex(msg), "wParam的值是:", wParam, " lParam的值是:", lParam)
print("wParam的值是窗口状态")
if wParam == win32con.SIZE_RESTORED:
print(" 窗口为创建时的尺寸:SIZE_RESTORED:", win32con.SIZE_RESTORED)
if wParam == win32con.SIZE_MINIMIZED:
print(" 窗口最小化:SIZE_MINIMIZED:", win32con.SIZE_MINIMIZED)
if wParam == win32con.SIZE_MAXIMIZED:
print(" 窗口最大化:SIZE_MAXIMIZED:", win32con.SIZE_MAXIMIZED)
if wParam == win32con.SIZE_MAXSHOW:
print(" 当其他某个窗口还原到原来的大小时,消息将发送到所有弹出窗口:SIZE_MAXSHOW:", win32con.SIZE_MAXSHOW)
if wParam == win32con.SIZE_MAXHIDE:
print(" 当其他某个窗口最大化时,消息将发送到所有弹出窗口:SIZE_MAXHIDE:", win32con.SIZE_MAXHIDE)
print("lParam的值是窗口尺寸数值:")
print(" lParam低位窗口客户区新宽度:", LOWORD(lParam))
print(" lParam高位窗口客户区新宽度:", HOWORD(lParam))
# endregion
return 0
case win32con.WM_PAINT:
#产生WN_PAINT消息的的情况 客户区被隐藏又重现 用户改变窗口大小 调用ScrollWindow或者ScrollDC函数 调用InvalidateRect或InvalidateRgn函数
#擦除覆盖的消息框或者对话框 下拉菜单出现与消失 显示工具提示
#收到该消息 必须使无效区域变为有效区域才返回 不然Windows会一直发生WM_PAINT消息 直到区域有效
# print("接收到绘制消息",msg)
hdc=user32.BeginPaint(hwnd,ctypes.byref(ps))
#hdc只是客户区无效区域
# print(ps)
# hdc=win32gui.GetDC(hwnd)
si.cbSize=ctypes.sizeof(si)
si.fMask=win32con.SIF_POS
#------------01 start --获取客户区画图------------------------
rect=structure1.RECT()
user32.GetClientRect(hwnd,ctypes.byref(rect))
print("客户区矩形:",rect.left,rect.top,rect.right,rect.bottom)
# region Description 绘制表格
# x=0
# for i in range(rect.right):
# x=x+100
# gdi32.MoveToEx(hdc,x,0,None)
# gdi32.LineTo(hdc,x,rect.bottom)
# if x>rect.right:
# break
# y=0
# for i in range(rect.bottom):
# y=y+100
# gdi32.MoveToEx(hdc,0,y,None)
# gdi32.LineTo(hdc,rect.right,y)
# if y>rect.bottom:
# break
# endregion
# region Description 任务02 绘制一个矩形-
#------------------任务02 绘制一个矩形----------------------------------------
ptArray=structure1.POINT *5
aarray= ptArray()
i=1
for ii in aarray:
ii.x=i+1
ii.y=i+2
i=i*2
for bb in aarray:
print("这是测试:",bb.x)
pt=[100,100,200,100,200,200,100,200,100,100]
gdi32.MoveToEx(hdc, pt[0], pt[1], None)
for i in range(len(pt)):
if i %2==0:
gdi32.LineTo(hdc,pt[i],pt[i+1])
#-------------------任务02结束 -------------------------------------
# endregion
# region Description 输出文字
#-------------01 end---------------
#获取垂直滚动框的位置
user32.GetScrollInfo(hwnd,win32con.SB_VERT,ctypes.byref(si))
Globalvariable1.global_int_VerticalScrollCurrentPosition=si.nPos
#获取水平滚动框的位置
user32.GetScrollInfo(hwnd,win32con.SB_HORZ,ctypes.byref(si))
Globalvariable1.global_int_HorizontalScrollCurrentPosition=si.nPos
iVertPos=Globalvariable1.global_int_VerticalScrollCurrentPosition
if Globalvariable1.global_cyChar !=0:
iPaintBeg=MAX(0,iVertPos+ps.rcPaint.top/Globalvariable1.global_cyChar)
num=Globalvariable1.global_int_displayHaveMaxLinesInClient
if Globalvariable1.global_cyChar!=0:
iPaintEnd=min(num-1,iVertPos+ps.rcPaint.bottom/Globalvariable1.global_cyChar)
print("字符宽度:",Globalvariable1.global_cxChar)
systemMetricsDicObject=systemMetricsDic1.systemMetrics()
# 要显示的内容在按照字体高度为一行计算有多少行
if Globalvariable1.global_int_displayHaveMaxLinesInClient==0:
Globalvariable1.global_int_displayHaveMaxLinesInClient=systemMetricsDicObject.leng()
print("输出显示内容具有多少行:",Globalvariable1.global_int_displayHaveMaxLinesInClient)
if Globalvariable1.global_cxChar>0:
for i in range(systemMetricsDicObject.leng()):
y=Globalvariable1.global_cyChar*(i-Globalvariable1.global_int_VerticalScrollCurrentPosition)
x=Globalvariable1.global_cxChar *(1-Globalvariable1.global_int_HorizontalScrollCurrentPosition)
#输出索引常量名
gdi32.TextOutW(hdc,x,y,systemMetricsDicObject.dicByIndex[i]["label"],len(systemMetricsDicObject.dicByIndex[i]["label"]))
#输出索引常量描述 x位置向右偏移约Globalvariable1.global_cxCaps*22
gdi32.TextOutW(hdc, x+Globalvariable1.global_cxCaps*44, y,
systemMetricsDicObject.dicByIndex[i]["describle"],
len(systemMetricsDicObject.dicByIndex[i]["describle"]))
value=user32.GetSystemMetrics(systemMetricsDicObject.dicByIndex[i]["index"])
# print("输出得到的结果:",value)
#右对齐
gdi32.SetTextAlign(hdc,win32con.TA_RIGHT|win32con.TA_TOP)
gdi32.TextOutW(hdc,x+Globalvariable1.global_cxCaps*44+40*Globalvariable1.global_cxChar,y,str(value),len(str(value)))
#恢复左对齐输出
gdi32.SetTextAlign(hdc, win32con.TA_LEFT | win32con.TA_TOP)
rect=win32gui.GetClientRect(hwnd)
win32gui.DrawTextW(hdc,"hello中国",len("hello中国"),rect,win32con.DT_SINGLELINE | win32con.DT_CENTER | win32con.DT_VCENTER)
# win32gui.ReleaseDC(hwnd,hdc)
#----------------任务1
# user32.MoveWindow(cvHwnd, 0, 0, rect[2], rect[3], True)
# endregion
# region Description 客户区中间绘制一条直线
lCyClient=Globalvariable1.global_cyClient
lCxClient=Globalvariable1.global_cxClient
gdi32.MoveToEx(hdc,0,lCyClient//2,None)
gdi32.LineTo(hdc,lCxClient,lCyClient//2)
# endregion
NUM=1000
pointsArray=structure1.POINT *NUM
#实例化数组
points=pointsArray()
i=0
for point in points:
point.x=int(i * lCxClient /NUM)
point.y=int((lCyClient/2) * (1-math.sin(2*math.pi * i/NUM)))
i=i+1
for p in points:
print("查看数组真实值:",p.x,p.y)
gdi32.Polyline(hdc,points,NUM)
user32.EndPaint(hwnd,ps)
return 0
case win32con.WM_NCPAINT:
print("这是非客户区消息")
case win32con.WM_SHOWWINDOW:
if debug:
print("收到一个WM_SHOWWINDOW显示窗口消息,通常由调用ShowWindow函数产生")
return 0
case win32con.WM_SYSCOMMAND:
if debug:
print("注意!!!收到一个系统命令,通常不要返回,继续让消息传给系统处理:否则属于屏蔽系统命令,WM_SYSCOMMAND消息编号是:",msg," ",hex(msg),"wParam的值是:",wParam," lParam的值是:",lParam)
case win32con.WM_LBUTTONDOWN:
if debug:
print("你单击了鼠标左键,消息编号是:",msg," ",hex(msg),"wParam的值是:",wParam," lParam的值是:",lParam)
return 0
case win32con.WM_RBUTTONDOWN:
if debug:
print("你单击了鼠标右键,消息编号是:",msg," ",hex(msg),"wParam的值是:",wParam," lParam的值是:",lParam)
return 0
case win32con.WM_MOUSEMOVE:
if debug:
pass
# print("你正在移动鼠标,消息编号是:",msg," ",hex(msg),"wParam的值是:",wParam," lParam的值是:",lParam)
return 0
case win32con.WM_MBUTTONDOWN:
if debug:
print("你单击了鼠标中键,消息编号是:",msg," ",hex(msg),"wParam的值是:",wParam," lParam的值是:",lParam)
return 0
case win32con.WM_LBUTTONDBLCLK:
if debug:
print("你双击了鼠标左键,消息编号是:",msg," ",hex(msg),"wParam的值是:",wParam," lParam的值是:",lParam)
return 0
case win32con.WM_KEYDOWN:
if debug:
print("你按下了键盘键,消息编号是:", msg, " ", hex(msg), "wParam的值是:", wParam, " lParam的值是:", lParam)
return 0
case win32con.WM_VSCROLL:
#wParam低位字是通知码 比如SB_LINEUP SB_LINELEFT SB_ENDSCROLL等 高位字是拖动滚动框时的当前位置
#当鼠标放在滚动框上按住鼠标 移动滚动框就会产生SB_THUMBTRACK和SB_THUMBPOSITION通知码消息和滚动消息
#在wParam低位字为SB_THUMBTRACK时 wParam高位字就是拖动滚动框时的当前位置 该值位于滚动条范围值之间
#如果wParame低位字是SB_THUMBPOSITION时 wParam的高位字就是用户释放鼠标键后滚动框的最终位置 如果不调用
#SetScrollPos来处理SB_THUMBTRACK或SB_THUMBPOSITION消息 用户释放鼠标后 滚动框会迅速调回原来位置
# region Description 垂直滚动条事件
si.cbSize=ctypes.sizeof(si)
si.fMask=win32con.SIF_ALL
user32.GetScrollInfo(hwnd,win32con.SB_VERT,ctypes.byref(si))
Globalvariable1.global_int_VerticalScrollCurrentPosition=si.nPos
#查看滚动条通知码
match LOWORD(wParam):
case win32con.SB_TOP:
si.nPos=si.nMin
case win32con.SB_BOTTOM:
si.nPos=si.nMax
case win32con.SB_LINEUP: #按向上滚动按钮
si.nPos -=1
case win32con.SB_LINEDOWN:
si.nPos += 1
case win32con.SB_PAGEUP:
si.nPos -=si.nPage
case win32con.SB_PAGEDOWN:
si.nPos +=si.nPage
case win32con.SB_THUMBPOSITION:
si.nPos=si.nTrackPos
case _:
pass
si.fMask=win32con.SIF_POS
user32.SetScrollInfo(hwnd,win32con.SB_VERT,ctypes.byref(si),True)
user32.GetScrollInfo(hwnd,win32con.SB_VERT,ctypes.byref(si))
if(si.nPos != Globalvariable1.global_int_VerticalScrollCurrentPosition):
a=Globalvariable1.global_cyChar
b=Globalvariable1.global_int_VerticalScrollCurrentPosition
user32.ScrollWindow(hwnd,0,a * (b-si.nPos),None,None)
user32.UpdateWindow(hwnd)
# endregion
return 0
case win32con.WM_HSCROLL:
# wParam低位字是通知码 比如SB_LINEUP SB_LINELEFT SB_ENDSCROLL等 高位字是拖动滚动框时的当前位置
# 当鼠标放在滚动框上按住鼠标 移动滚动框就会产生SB_THUMBTRACK和SB_THUMBPOSITION通知码消息和滚动消息
# 在wParam低位字为SB_THUMBTRACK时 wParam高位字就是拖动滚动框时的当前位置 该值位于滚动条范围值之间
# 如果wParame低位字是SB_THUMBPOSITION时 wParam的高位字就是用户释放鼠标键后滚动框的最终位置 如果不调用
# SetScrollPos来处理SB_THUMBTRACK或SB_THUMBPOSITION消息 用户释放鼠标后 滚动框会迅速调回原来位置
# region Description 水平滚动条事件
si.cbSize = ctypes.sizeof(si)
si.fMask = win32con.SIF_ALL
user32.GetScrollInfo(hwnd, win32con.SB_HORZ, ctypes.byref(si))
Globalvariable1.global_int_HorizontalScrollCurrentPosition = si.nPos
# 查看滚动条通知码
match LOWORD(wParam):
case win32con.SB_LEFT:
si.nPos = si.nMin
case win32con.SB_RIGHT:
si.nPos = si.nMax
case win32con.SB_LINELEFT: # 按向上滚动按钮
si.nPos -= 1
case win32con.SB_LINERIGHT:
si.nPos += 1
case win32con.SB_PAGELEFT:
si.nPos -= si.nPage
case win32con.SB_PAGERIGHT:
si.nPos += si.nPage
case win32con.SB_THUMBPOSITION:
si.nPos = si.nTrackPos
case _:
pass
si.fMask = win32con.SIF_POS
user32.SetScrollInfo(hwnd, win32con.SB_HORZ, ctypes.byref(si), True)
user32.GetScrollInfo(hwnd, win32con.SB_HORZ, ctypes.byref(si))
if (si.nPos != Globalvariable1.global_int_HorizontalScrollCurrentPosition):
a = Globalvariable1.global_cxChar
b = Globalvariable1.global_int_HorizontalScrollCurrentPosition
user32.ScrollWindow(hwnd, 0, a * (b - si.nPos), None, None)
user32.UpdateWindow(hwnd)
# endregion
return 0
case win32con.WM_DESTROY:
user32.PostQuitMessage(0)
if debug:
print("程序终止")
return 0
return win32gui.DefWindowProc(hwnd, msg, wParam, lParam)
def cancelWindowCaptionBar(hwnd):
dwStyle = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE)
dwStyle ^= win32con.WS_MINIMIZEBOX # 设置窗体取消最小化按钮
dwStyle ^= win32con.WS_MAXIMIZEBOX # 设置窗体取消最大化按钮
dwStyle ^= win32con.WS_DLGFRAME # 设置窗体取消标题栏边框
# dwStyle &= ~(win32con.WS_SIZEBOX) #取消调整大小
win32api.SetWindowLong(hwnd, win32con.GWL_STYLE, dwStyle) # 设置新的风格
className = 'MyWindowClass'
#---------第一步 初始化类结构-python版本和C语言版本有一定的不同 某些域没有cbClsExtra-----------------------------------------
#---------窗口类的作用就是定义窗口的一般性特征 或者通用特征
# region Description 第一步声明类 根据类以便创建窗口
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 = className
# endregion
#--------第二步 注册类---------------------------------------------
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(className, "我的窗口", 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)
#测试使用格式化函数wsprintfW
# region Description 测试函数使用
user32.wsprintfW(pStringBuffer,"this is %d test %d,%d",ctypes.c_int32(5).value,ctypes.c_int32(7).value,ctypes.c_int32(5).value)
print("缓冲字符串:",pStringBuffer)
s=""
for i in pStringBuffer:
# print(i,end='')
if i != b'\x00':
s=s+i.decode('utf-8')
print(s)
# print(i.decode('utf-8'),end="")
print("测试全局变量的Globalvariable1.global_cxChar值",Globalvariable1.global_cxChar)
# endregion
#第六步 自动执行消息队列 msg由操作系统自动生成 传递给你的程序
while user32.GetMessageW(ctypes.byref(msg), None, wParam, lparam) != 0:
user32.TranslateMessage(ctypes.byref(msg))
user32.DispatchMessageW(ctypes.byref(msg))
演示效果: