Skip to main content

实用案例

查找句柄

win32gui.findwindow(class_name:str, window_text:str) -> int

根据pid获取句柄

import win32gui
import win32process

def get_hwnds_for_pid(pid):
# 通过PID查询句柄ID
def callback(hwnd, hwnds):
if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
_, found_pid = win32process.GetWindowThreadProcessId(hwnd)
if found_pid == pid:
hwnds.append(hwnd)
return True

hwnds = []
win32gui.EnumWindows(callback, hwnds)
hwndy = 0
if hwnds:
hwndy = hwnds[0]

return hwndy


if __name__ in "__main__":
get_hwnds_for_pid(传入PID值) # 传入PID值后返回句柄ID

查找子窗口或父窗口

win32gui.findwindow(
parent_hwnd:int|None, # 以父窗口向下查找子窗口,此时子窗口可以设置为None
child_hwnd:int|None, # 以子窗口下上查找父窗口,此时父窗口可以设置为None
class_name:str,
window_text:str
) -> int

向窗体发送指令

win32gui.SendMessage(
hWnd:int,
Msg:str, # 要发送的消息,这些消息都是windows预先定义好的
wParam:wParam, # 按照消息的要求进行设置
lParam:lParam # 按照消息的要求进行设置
)

预先定义好的Msg

两者区别

  • PostMessageW只是通知窗口有个消息要处理,发送消息后不会等待消息处理结果直接返回
  • SendMessage是通知窗口有个消息要立即处理,并等待处理结果再返回,二者在发送消息时没有区别

wParamlParam都需要按照消息的要求进行设置。每个消息的要求都不一样,具体如何设置,需要查看对应消息的文档。

枚举窗口

Hwnd = NewType('hWnd', int)
def enum_windows() -> Hwnd_List:
hwnd_list:Hwnd_List = []
win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), hwnd_list)

return hwnd_list

获取窗体尺寸

def get_info_by_hwnd(hwnd:Hwnd, context_hwnd:Optional[Hwnd]=None) -> HwndInfo:
"""
@Description 获取窗体详细信息

- param hwnd :{Hwnd} 目标窗体
- param context=None :{Optional[Hwnd]} 指定内容区窗口句柄,一般是子窗口句柄

@returns `{dict}` {description}
```python
{
"hwnd":窗口句柄,
"window_width":完整窗体宽度,
"window_height":完成窗体高度,
'context_width':真实内容区宽度,
'context_height':真实内容去高度,
'border':左右下边宽,一般只有上边宽不一样,
'border_top':上边宽高度,
'window_left':窗体位置,
'window_top':int,
'window_right':int,
'window_bottom':int
}
"""
window_left, window_top, window_right, window_bottom = win32gui.GetWindowRect(hwnd)
window_width = window_right - window_left
window_height = window_bottom - window_top

if not context_hwnd:
context_hwnd = hwnd

context_left, context_top, context_right, context_bottom = win32gui.GetClientRect(context_hwnd)
context_width = context_right - context_left
context_height = context_bottom - context_top

border = (window_width - context_width) // 2
border_top = (window_height - context_height) - border

result = {
"hwnd":hwnd,
"window_width":window_width,
"window_height":window_height,
'context_width':context_width,
'context_height':context_height,
'border':border,
'border_top':border_top,
'window_left':window_left,
'window_top':window_top,
'window_right':window_right,
'window_bottom':window_bottom,
}

return result

前置窗口

win32gui.SetForegroundWindow(hwnd)  # 设置前置窗口

窗口最小化

def is_mini(hwnd: int) -> bool:
from win32gui import IsIconic

try:
return IsIconic(hwnd) != 0

except Exception as err:
return False

return False

关闭窗口

win32gui.PostMessage(
hwnd:int,
win32con.WM_CLOSE, # 功能代码
0,
0)

关闭程序(pid)

import win32gui
import win32process
import psutil



def handle_window(hwnd, extra):
if win32gui.IsWindowVisible(hwnd):

if '程序标题' in win32gui.GetWindowText(hwnd): # 判断是否符合
_,PID = win32process.GetWindowThreadProcessId(hwnd) # 通过句柄ID查询进程PID(第0个元素不管,第1个元素是PID)
p = psutil.Process(PID) # 实例化PID
p.terminate() # 关闭PID进程


if __name__ in "__main__":
win32gui.EnumWindows(handle_window, None) # 通过句柄ID查询PID并关闭PID

显示窗口

win32gui.ShowWindow(
hwnd:int,
win32con.SW_SHOWNORMAL # 系统内置的参数
)
  • SW_HIDE:隐藏窗口并激活其他窗口。nCmdShow=0。
  • SW_SHOWNORMAL:激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志。nCmdShow=1。
  • SW_SHOWMINIMIZED:激活窗口并将其最小化。nCmdShow=2。
  • SW_SHOWMAXIMIZED:激活窗口并将其最大化。nCmdShow=3。
  • SW_SHOWNOACTIVATE:以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态。nCmdShow=4。
  • SW_SHOW:在窗口原来的位置以原来的尺寸激活和显示窗口。nCmdShow=5。
  • SW_MINIMIZE:最小化指定的窗口并且激活在Z序中的下一个顶层窗口。nCmdShow=6。
  • SW_SHOWMINNOACTIVE:窗口最小化,激活窗口仍然维持激活状态。nCmdShow=7。
  • SW_SHOWNA:以窗口原来的状态显示窗口。激活窗口仍然维持激活状态。nCmdShow=8。
  • SW_RESTORE:激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。nCmdShow=9。
  • 用例
def _hide_w32_window(self):
try:
w32win = win32gui.FindWindow(None, self.title)
win32gui.ShowWindow(w32win, SW_HIDE)
win32gui.SetWindowLong(
w32win,
GWL_EXSTYLE,
win32gui.GetWindowLong(
w32win, GWL_EXSTYLE) | WS_EX_TOOLWINDOW
)
win32gui.ShowWindow(w32win, SW_SHOW)
self._return_focus_w32()
except Exception:
tb = traceback.format_exc()
Logger.error(
'Notification: An error occured in {}\n'
'{}'.format(self.title, tb)
)
def Show(self):
win32gui.ShowWindow(self.hwnd, win32con.SW_SHOW)

根据进程标题名称隐藏运行进程

import win32gui

from win32.lib import win32con


def handle_window(hwnd, extra):
if win32gui.IsWindowVisible(hwnd):

if '需要隐藏的程序标题名称' in win32gui.GetWindowText(hwnd):
win32gui.ShowWindow(hwnd, win32con.SW_HIDE)


if __name__ == '__main__':
win32gui.EnumWindows(handle_window, None)

# win32gui.EnumWindows(枚举函数名称, None) 语句是进行句柄ID枚举
# win32gui.GetWindowText(句柄ID) 语句是通过句柄ID来获取进程名称
# win32gui.IsWindowVisible(句柄ID) 语句是查询此句柄ID是否存在,存在返回1 否则返回0
# win32gui.ShowWindow(句柄ID, win32con.SW_HIDE) 语句是通过指定句柄ID来隐藏进程

操作Word

import win32com  #引入使用
from win32com import client as com #引入使用

# 必须使用内部的client的dispatch方法
from win32com import client as wordApi

# 引入ms的officeAPI进行word的操作
word = wordApi.dispatch('Word.Application')

# 打开word文件的路径
doc = word.Document.Open(path)

# 保存/转换文件, 16是必须添加的参数
doc.SaveAs(path,16)

鼠标操作

案例1(失败)

def Mouse_RB(self,str_app,lb_dx,lb_dy,Flag='1'):
print "*********Mouse_RB function**********"
time.sleep(1)
tmp=(string.atoi(lb_dx),string.atoi(lb_dy))
hwnd = win32gui.FindWindow(None, str_app)
print 'Mouse_RB str_app,hwnd ',str_app,hwnd
if hwnd < 1:
hwnd = self.find_main_window(str_app)
#win32gui.ShowWindow(hwnd, 0)
win32api.SetCursorPos(tmp)
print 'Mouse_RB tmp =',tmp
if Flag == '1':
time.sleep(1)
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN,tmp[0], tmp[1])
time.sleep(0.05)
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP,tmp[0], tmp[1])
time.sleep(0.05)
return True

def Mouse_LB(self,str_app,lb_dx,lb_dy,Flag='1'):
print "*********Mouse_LB function**********"
time.sleep(1)
tmp=(string.atoi(lb_dx),string.atoi(lb_dy))
hwnd = win32gui.FindWindow(None, str_app)
if hwnd < 1:
hwnd = self.find_main_window(str_app)
#win32gui.ShowWindow(hwnd, 0)
win32api.SetCursorPos(tmp)
print 'Mouse_LB tmp =',tmp
if Flag == '1':
time.sleep(1)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,tmp[0], tmp[1])
time.sleep(0.05)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,tmp[0], tmp[1])
time.sleep(0.05)
return True

后台截图

案例1

(部分逻辑参考自onmyoji_bot)

1、获取窗体信息

import win32gui, win32con, win32api, win32ui
import typing

# (x,y) 常用坐标类型
coord = typing.NewType('tuple(int,int)', tuple)

# 获取窗体信息
def get_context_size(hwnd:int=0) -> coord:
"""
@Description {description}

- param hwnd=0 :{int} {description}

returns `{coord}` {description}

```python
(
"border":"窗体的边宽",
"border_top":"窗体菜单栏的高度",
"c_width":"客户端的内容宽度",
"c_height":"客户端的内容高度"
)
"""

# 窗体的尺寸
w_left, w_top, w_right, w_bot = win32gui.GetWindowRect(hwnd)
w_width = w_right - w_left
w_height = w_bot - w_top

# 内容区的尺寸
c_left, c_top, c_right, c_bot = win32gui.GetClientRect(hwnd)
c_width = c_right - c_left
c_height = c_bot - c_top

# 计算边框的宽度
border = (w_width - c_width) // 2
# 计算菜单栏的高度
border_top = (w_height - c_height) - border

return (border, border_top, c_width, c_height)

def window_capture(hwnd, filename=None, mode="file", region=None):
"""
@Description 截取指定窗口句柄的图片

- param hwnd :{param} {description}
- param filename=None :{param} {description}
- param mode=win32 :{string} 采用哪种方式:file|PIL|cv2

returns `{}` {description}

"""

# 获取该窗体的内容区范围
X = Y = 0
border, border_top, w,h = get_context_size(hwnd)

# 范围截图
if region:
startX, startY, endX, endY = region
x = startX + border
y = startY + border_top
w = endX - startX
h = endY - startY
else:
# 完整截图
x = border
y = border_top

# 取设备上下文DC(Divice Context)
hwndDC = win32gui.GetWindowDC(hwnd)
# 根据窗口的DC获取mfcDC
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
# 创建可兼容的DC
saveDC = mfcDC.CreateCompatibleDC()

# 创建bigmap用来存放图片信息对内存对象
BitMap = win32ui.CreateBitmap()
BitMap.CreateCompatibleBitmap(mfcDC, w, h)

# 截取图片到内存
saveDC.SelectObject(BitMap)

saveDC.BitBlt(
(X, Y), # 开始截取的位置
(w, h), # 内容的尺寸 GetClientRect 的宽和高
mfcDC,
(x, y), # 偏移,一般将窗口的border和上方菜单栏高度输入即可
win32con.SRCCOPY)

res = BitMap
if mode == "file":
# 保存为本地文件
res = bitMap_to_file(BitMap, DC=saveDC, filename=filename)

elif mode == "PIL":
res = bitMap_to_PIL_img(BitMap)
if res: res.show()

elif mode == "cv2":
res = bitMap_to_cv2_img(BitMap, w, h)

# 释放资源
win32gui.DeleteObject(BitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
return res

3、处理截取到的图片

3.1 保存图片到文件(win32api)

def bitMap_to_file(BitMap, DC, filename) -> bool:
try:
BitMap.SaveBitmapFile(DC, filename)
return True
except:
return False

3.2 保存为PIL文件对象

from PIL import Image

def bitMap_to_PIL_img(BitMap) -> Image:
try:
# 提取当前截取的图片信息
bitMapInfo = BitMap.GetInfo()

# 转换为buffer供pil使用
bitMapStr = BitMap.GetBitmapBits(True)

# 通过buffer生成pil的图片对象实例
img_PIL = Image.frombuffer(
'RGB',
(bitMapInfo['bmWidth'],
bitMapInfo['bmHeight']),
bitMapStr,'raw','BGRX',
0,
1)

return img_PIL
except:
return None

3.3保存为 cv2 图片对象

import cv2
import numpy as np

def bitMap_to_cv2_img(BitMap, w, h, gray=False):
signedIntsArray = BitMap.GetBitmapBits(True)
img = np.frombuffer(signedIntsArray, dtype='uint8')
img.shape = (h, w, 4)

# 灰度处理
if gray:
return cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
else:
return cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)

案例2

#对后台窗口截图
import win32gui, win32ui, win32con
from ctypes import windll
from PIL import Image
import cv2
import numpy

#获取后台窗口的句柄,注意后台窗口不能最小化
#窗口的类名可以用Visual Studio的SPY++工具获取
hWnd = win32gui.FindWindow("NotePad",None)

#获取句柄窗口的大小信息
left, top, right, bot = win32gui.GetWindowRect(hWnd)
width = right - left
height = bot - top

#返回句柄窗口的设备环境,覆盖整个窗口,包括非客户区,标题栏,菜单,边框
hWndDC = win32gui.GetWindowDC(hWnd)

#创建设备描述表
mfcDC = win32ui.CreateDCFromHandle(hWndDC)

#创建内存设备描述表
saveDC = mfcDC.CreateCompatibleDC()

#创建位图对象准备保存图片
saveBitMap = win32ui.CreateBitmap()

#为bitmap开辟存储空间
saveBitMap.CreateCompatibleBitmap(mfcDC,width,height)

#将截图保存到saveBitMap中
saveDC.SelectObject(saveBitMap)

#保存bitmap到内存设备描述表
saveDC.BitBlt((0,0), (width,height), mfcDC, (0, 0), win32con.SRCCOPY)

#如果要截图到打印设备:
###最后一个int参数:0-保存整个窗口,1-只保存客户区。如果PrintWindow成功函数返回值为1
#result = windll.user32.PrintWindow(hWnd,saveDC.GetSafeHdc(),0)
#print(result) #PrintWindow成功则输出1

#保存图像
##方法一:windows api保存
###保存bitmap到文件
saveBitMap.SaveBitmapFile(saveDC,"img_Winapi.bmp")

##方法二(第一部分):PIL保存
###获取位图信息
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
###生成图像
im_PIL = Image.frombuffer('RGB',(bmpinfo['bmWidth'],bmpinfo['bmHeight']),bmpstr,'raw','BGRX',0,1)
##方法二(后续转第二部分)

##方法三(第一部分):opencv+numpy保存
###获取位图信息
signedIntsArray = saveBitMap.GetBitmapBits(True)
##方法三(后续转第二部分)

#内存释放
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hWnd,hWndDC)

##方法二(第二部分):PIL保存
###PrintWindow成功,保存到文件,显示到屏幕
im_PIL.save("im_PIL.png") #保存
im_PIL.show() #显示

##方法三(第二部分):opencv+numpy保存
###PrintWindow成功,保存到文件,显示到屏幕
im_opencv = numpy.frombuffer(signedIntsArray, dtype = 'uint8')
im_opencv.shape = (height, width, 4)
cv2.cvtColor(im_opencv, cv2.COLOR_BGRA2RGB)
cv2.imwrite("im_opencv.jpg",im_opencv,[int(cv2.IMWRITE_JPEG_QUALITY), 100]) #保存
cv2.namedWindow('im_opencv') #命名窗口
cv2.imshow("im_opencv",im_opencv) #显示
cv2.waitKey(0)
cv2.destroyAllWindows()


https://blog.csdn.net/zhuisui_woxin/article/details/84345036

案例3

def window_capture(hwnd):
hwndDC= win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
rctA = win32gui.GetWindowRect(hwnd)
w = rctA[2] - rctA[0]
h = rctA[3] - rctA[1]
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
saveDC.BitBlt((0, 0), (w, h), mfcDC, (0, 0), win32con.SRCCOPY)
signedIntsArray = saveBitMap.GetBitmapBits(True)
img = np.frombuffer(signedIntsArray, dtype="uint8")
img.shape = (h, w, 4)
win32gui.DeleteObject(saveBitMap.GetHandle())
mfcDC.DeleteDC()
saveDC.DeleteDC()
return cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)

桌面截图

# 截图整个桌面
import win32gui
import win32ui
import win32con
import win32api

# 获取桌面
hdesktop = win32gui.GetDesktopWindow()
# 分辨率适应
width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
# 创建设备描述表
desktop_dc = win32gui.GetWindowDC(hdesktop)
img_dc = win32ui.CreateDCFromHandle(desktop_dc)
# 创建一个内存设备描述表
mem_dc = img_dc.CreateCompatibleDC()
# 创建位图对象
screenshot = win32ui.CreateBitmap()
screenshot.CreateCompatibleBitmap(img_dc, width, height)
mem_dc.SelectObject(screenshot)
# 截图至内存设备描述表
mem_dc.BitBlt((0, 0), (width, height), img_dc, (0, 0), win32con.SRCCOPY)
# 将截图保存到文件中
screenshot.SaveBitmapFile(mem_dc, 'screenshot.bmp')
# 内存释放
mem_dc.DeleteDC()
win32gui.DeleteObject(screenshot.GetHandle())

列出当前显示器

import win32api

MoniterDev = win32api.EnumDisplayMonitors(None, None)
print("MoniterDev: ", len(MoniterDev))

显示屏幕尺寸和放大比例

获取屏幕的真实分辨率,放大比例,显示分辨率等信息

from win32 import win32api, win32gui, win32print
from win32.lib import win32con
from pydantic import BaseModel


class ScreenType(BaseModel):
show_width: int
show_height: int
real_width: int
real_height: int
scale_rate: int


def getSrceen() -> ScreenType | None:
"""
@Description 获取屏幕的真实尺寸和放大比例
@returns `{ ScreenType | None}` {description}
@example
```py
screen_size = getSrceen()
```
"""
try:
"""获取缩放后的分辨率"""
sX = win32api.GetSystemMetrics(0) # 获得屏幕分辨率X轴
sY = win32api.GetSystemMetrics(1) # 获得屏幕分辨率Y轴

"""获取真实的分辨率"""
hDC = win32gui.GetDC(0)
w = win32print.GetDeviceCaps(hDC, win32con.DESKTOPHORZRES) # 横向分辨率
h = win32print.GetDeviceCaps(hDC, win32con.DESKTOPVERTRES) # 纵向分辨率

# 缩放比率
scale_rate = round(w / sX, 2)

return ScreenType(
show_width=sX,
show_height=sY,
real_width=w,
real_height=h,
scale_rate=scale_rate,
)

except Exception as e:
print("getSrceen fail", e)
return None

粘贴板操作

复制PIL图片到粘贴板

http://t.zoukankan.com/enumx-p-12359863.html

引入依赖

import win32clipboard
from PIL import Image
from io import BytesIO

def copy_img_to_clipboard(type_data, data):
# 打开粘贴板
win32clipboard.OpenClipboard()

# 清空粘贴板
win32clipboard.EmptyClipboard()

# 将数据放到粘贴板
win32clipboard.SetClipboardData(type_data, msg)

# 关闭粘贴板
win32clipboard.CloseClipboard()

PIL

def pil_img_to_byte(img_path:str):

image = Image.open(file_img)

# 创建一个字节对象
data_byte = BytesIO()

# 保存未bmp格式,将数据保存到为字节对象
image.save(data_byte, 'BMP')

# BMP图片有14字节的header,需要额外去除
data = data_byte.getvalue()[14:]

# 关闭字节对象
data_byte.close()

# DIB: 设备无关位图(device-independent bitmap),名如其意
# BMP的图片有时也会以.DIB和.RLE作扩展名
# 设置好剪贴板的数据格式,再传入对应格式的数据,才能正确向剪贴板写入数据
copy_img_to_clipboard(win32clipboard.CF_DIB, data)

CV2

def cv2_img_to_byte(img_path: str) -> bool:
try:
img = cv2.imread(img_path)
img_to_bytes_res = cv2.imencode(".bmp", img)

if img_to_bytes_res[0]:
img_byte = img_to_bytes_res[1].tobytes()
bmp_data = img_byte[14:]
copy_img_to_clipboard(bmp_data)
return True

return False

except Exception as e:
print("cv2_img_to_byte fail ", e)
return False

word转pdf

import os, pathlib

from win32com import client
from pydantic import BaseModel, Field


class WORD_CODE: # 记录所有word代码
pdf: int = 17


class CpsWordConverterConfig(BaseModel):
overwrite: bool = Field(False, description="如果文件已存在,是否覆盖输出")
show_details: bool = Field(False, description="是否打印错误")


class CpsWordConverter:
def __init__(self, config: CpsWordConverterConfig = None):
"""
@Description {description}

- param config=None :{CpsWordConverterConfig} 实例配置

@example
```python
target = r"Z:/xxxdir/xxxx.docx"
target_dir = r"Z:/work/2023/改图/2023职称/周末bk"

# 初始化
Config = CpsWordConverterConfig(overwrite=False, show_details=True)
Convert = CpsWordConverter(Config)

# 单独转换文件
Convert.convert(target)

# 遍历目录
Convert.convert(target_dir)
```

"""
# 检查当前系统环境是否安装了word
if not self.__check():
raise "实例化失败"

if config:
self.config = config
else:
self.config = CpsWordConverterConfig()

self.word = None # 存储word实例

def print(self, *argvs, **keys):
if self.config.show_details:
print(*argvs, **keys)

def convert(self, target: str) -> list[str]:
"""
@Description 转换主函数

- param target :{str} target可以是目录,也可以是文件,如果是目录,将直接进行递归

"""
try:
if not os.path.exists(target):
self.print("目标不存在: ", target)
return []

# 判断是目录还是文件
p = pathlib.Path(target)
result = []
if p.is_file():
result.append(self.__word2pdf(p))
elif p.is_dir():
result = self.__dir_handler(p)

return result
except Exception as e:
self.print("convert err: ", e)
return []

def __del__(self):
if self.word:
self.word.Quit()
self.word = None

def __check(self) -> bool:
return True

def __open_word(self):
self.word = client.Dispatch("Word.Application")
self.word.Visible = False

return self.word

def __dir_handler(self, dir_path: str | pathlib.Path) -> str:
docx = list(pathlib.Path(dir_path).glob("**/*.docx"))
doc = list(pathlib.Path(dir_path).glob("**/*.doc"))

word_list = docx + doc

result = []
if len(word_list) == 0:
return ""
else:
self.print("当前需要处理的文件有: ", len(word_list))

for each in word_list:
result.append(self.__word2pdf(each))

return result

@staticmethod
def word_accept_all_revisions(word):
"""
@Description 对当前的word文件进行一次接收所有修订操作,并原地保存

- param word :{param} word实例

"""
try:
word.ActiveDocument.TrackRevisions = False
# word.WordBasic.AcceptAllChangesInDoc() # 会报错,但也可以接收所有修订
word.ActiveDocument.Revisions.AcceptAll()

if word.ActiveDocument.Comments.Count >= 1:
word.ActiveDocument.DeleteAllComments()

return word
except Exception as err:
print("word_accept_all_revisions: ", err)
return word

def __word2pdf(self, word_file: pathlib.Path) -> str:
"""
- param word_file :{str} `.doc|.docx`结尾的文件
"""
try:
# 获取同名的pdf输出路径
word_file_path = str(word_file.resolve())
output_pdf_path = str(word_file.resolve()).replace(word_file.suffix, ".pdf")

# 已存在pdf,是否进行覆盖
if os.path.exists(output_pdf_path):
# 不覆盖的话,直接跳过
if not self.config.overwrite:
self.print("文件已存在: ", output_pdf_path)
return ""

# 打开word应用程序
if not self.word:
self.word = self.__open_word()

# 打开word文件
doc = self.word.Documents.Open(word_file_path)

# 所有修订
self.word_accept_all_revisions(self.word)

# 另存为后缀为".pdf"的文件,其中参数17表示为pdf
doc.Activate()
doc.SaveAs(output_pdf_path, WORD_CODE.pdf)

# 关闭原来word文件
doc.Close()

self.print("完成转换: ", word_file)
return output_pdf_path

except Exception as e:
self.print("word2pdf err: ", e)

return ""


def example():
target = r"Z:\work\2024\项目\东莞海腾码头补办水利手续\0808珠江水文水资源勘测中心-东莞海腾码头补办水利手续工作大纲及报价书(1).docx"
target_dir = r"Z:\work\2023\改图\2023职称\周末bk"

Config = CpsWordConverterConfig(overwrite=False, show_details=True)
Convert = CpsWordConverter(Config)
Convert.convert(target)


def main(target: str):
Config = CpsWordConverterConfig(overwrite=False, show_details=True)
Convert = CpsWordConverter(Config)
Convert.convert(target)


if __name__ == "__main__":
# example()
import argparse

# 初始化实例
parser = argparse.ArgumentParser()

# 解释参数
parser.add_argument(
"target", # 不要处理的word文件
help="需要处理的.doc/.docx文件,绝对路径",
type=str,
)

args = parser.parse_args()

main(args.target)