python安装轮子_我现在才知道,原来Python造轮子是这样实现的……

本文介绍如何利用Windows API和Python的ctypes模块创建三个实用的功能:获取屏幕像素颜色、模拟窗口点击及获取窗口尺寸。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

斌哥说

大家好,我是斌哥。

一说起造轮子,相信大家和我一样会感觉毛骨悚然。

就拿一个WindowSDK提供的MAKELONG宏来说。

它的定义是这样的,在C中:

#define MAKELONG(a, b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) << 16))

看着就很头大,没关系,下面斌哥就来说说怎么通过WindowsAPI来造出我们Python上能用的“轮子”。

来看看轮子怎么造,大致分为这样几步:

定义功能 ->> 调用windowsAPI ->> 实现功能

分别实现3个功能:

1、取得屏幕某个像素点的颜色2、给窗口发送点击消息3、获得窗口大小实际上,python给我们提供了和C交互的模块ctypes,在这个模块中我们可以快速的C语言结构的dll打交道。

我们都知道,api被封装在dll中,在windows系统上,所有的用户过程,其实都是调用了api在运行。

常用的几个装有API的dll文件也被ctypes写成类,它们分别是user32.dll和kernel32。

调用方式:

import ctypes

ctypes.windll.user32.user32内提供的函数

ctypes.windll.kernel32.kernel32内提供的函数

如果是调用其他dll则使用LoadLibrary,例如ctypes.windll.LoadLibrary(r"c:\my.dll")

先来看看第一个需求:取得屏幕某个像素点的颜色

我们要用到的接口是gdi.dll提供的GetPixel,其原型为:

COLORREFGetPixel(HDChdc,intnXPos,intnYPos);

GetPixel原形

在使用GetPixel之前,我们还必须获得当前屏幕设备句柄。

通过GetDC可以获得设备句柄,当窗口句柄参数为NULL时返回当前屏幕设备句柄。

GetDC原形

GetPixel可以获得指定坐标的像素颜色。

成功则返回一个RGB颜色的数字。

HDC hdc是一个设备上下文句柄,nXPos和nYPos对应xy坐标

import ctypes

def getpix_color(x,y):

if(type(x) != int or type(y) != int): #类型判断

return ""

dc = ctypes.windll.user32.GetDC(None)

return hex(ctypes.windll.LoadLibrary("gdi32.dll").GetPixel(dc,x,y))

需求1示例

第二个需求:给窗口发送点击消息。

这需要用到SendMessage或PostMessage,它们的原型是一样的,SendMessage和PostMessage的区别在于:SendMessage发送一个消息给窗口的消息队列,然后等待窗口执行,PostMessage则是发送到消息队列后立即返回,就像TCP和UDP的区别。

SeneMessage原形

我们要做的是:给窗口发送WM_LBUTTONDOWN和WM_LBUTTONUP消息(分别是鼠标左键按下和松开)

lParam参数为需要点击的坐标

因此我们要用到前面的MAKELONG这个宏来把坐标x和y转换成一个长整型,然后再取指针

但是Python并没有类似MAKELONG这样的玩意,怎么办呢?

我们自己造:

代码分析:

再来看看这个宏定义#define MAKELONG(a, b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) << 16))

可以看到这段定义先是把参数a转换成WORD类型,然后把b转换成WORD类型,然后再把b转换成DWORD类型,然后 a | b << 16 ,然后再转换成LONG类型

相当于:

a = (WORD)a

b = (WORD)b

b = (DWORD)b

(LONG)(a | b << 16)

Python的ctypes提供了C语言数据类型的基础类。

所有C类型的类都提供了1个value属性

c_数据类型名

例如:c_int c_long c_float

指针类型:ctypes只提供了2种指针类型,c_void_p和c_char_p

不过ctypes提供取地址的操作

无符号:c_u数据类型名 例如c_ulong c_ushort

以上各种类型的定义为:

typedef unsigned short WORD

typedef unsigned long DWORD

执行这样的操作,在Python中我们需要这样做:

import ctypes

def makelong(a,b): #转换成long

return ctypes.c_long(ctypes.c_ushort(a).value | ctypes.c_ulong(ctypes.c_ushort(b).value).value << 16).value

CLICK_LEFT_DOWN = 0x201

CLICK_LEFT_UP = 0x202

def click(hwnd,x,y): #给窗口发送左键点击消息

xy = makelong(x,y)

PostMessage(hwnd,CLICK_LEFT_DOWN,None,xy)

PostMessage(hwnd,CLICK_LEFT_UP,None,xy)

#传入窗口句柄和需要点击的按钮坐标即可执行左键点击

第三个需求:获得窗口大小。

要说明一点,这里的窗口大小是窗口客户区的大小,所以用到的是user32.dll提供的GetClientRect接口。

GetClientRect原形

BOOLGetClientRect(HWNDhWnd,LPRECTlpRect);

获得窗口的客户区大小,这里需要一个窗口句柄和RECT结构体指针。成功返回True,否则False

OK,那么我们在Python中只能自己写一个RECT结构体了

在Python中,结构体都是通过继承ctypes的Structure类来定义的

并且子类必须要有_fields_ 属性,该属性必须是2元素元组的列表,里面包含字段名和字段类型。

RECT结构体定义如下:

typedef struct _RECT {LONGleft;LONGtop;LONGright;LONGbottom;} RECT;

可以看到所有的结构体成员都是long类型。

那么我们在Python中定义结构体则是这样:

import ctypes

class my_Rect(ctypes.Structure):

_fields_ = [("x",ctypes.c_long),\

("y",ctypes.c_long),\

("width",ctypes.c_long),\

("height",ctypes.c_long)\

]

只要结构相同,成员名我们可以自己定义。

用pointer方法取出对象地址

def get_Client_Size(hwnd):

rect = my_Rect()

if(

bool(ctypes.windll.user32.GetClientRect( hwnd,ctypes.pointer(rect) ) )

):

return tuple( (rect.width,rect.height) ) #以元组方式返回宽高

return None

需求3示例

运行结果

我是斌哥,喜欢请点击关注。

斌哥说Python,只专注于Python技术!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值