- 功能描述
- 功能拆解
- 技术方案
- 依赖
- tkinter
- 全屏显示窗口
- 监听鼠标动作
- 显示按钮到指定位置
- pillow
- 截取全屏截图
- 绘制矩形
- 裁剪图片
- ctypes
- windows剪贴板函数
- ctypes调用
- 执行流程
- 具体实现
运行脚本后用鼠标拖拽选择屏幕上需要截图的区域,确认后将选中的区域放到剪贴版中,可粘贴其他程序中进行使用
功能拆解截取屏幕画面
获取鼠标移动、点击、位置信息
裁剪图片
实时显示选中的部分
将图片添加到剪切板
tkinter 负责显示和处理鼠标的操作
全屏显示窗口# 置顶
root.attributes('-topmost', True)
# 全屏
root.attributes('-fullscreen', True)
监听鼠标动作
label.bind('', self.onHoldLeftButton)
label.bind('', self.onHoldLeftButtonMove)
显示按钮到指定位置
toolkit.place(x=self.end_x, y=self.end_y, anchor='ne')pillow
pillow 负责图片操作
截取全屏截图ImageGrab.grab()绘制矩形
dImage = ImageDraw.Draw(img) dImage.rectangle(pos, outline='red', width=2)裁剪图片
image.crop(pos)ctypes
ctypes 调用Windows原生API操作剪切板
windows剪贴板函数HANDLE SetClipboardData( [in] UINT uFormat, [in, optional] HANDLE hMem );
文档
ctypes调用user32 = ctypes.windll.user32 user32.SetClipboardData.argtypes = [UINT, HANDLE] user32.SetClipboardData.restype = HANDLE user32.SetClipboardData(type, ctypes.c_char_p(alloc))执行流程
- 截取全屏截图,全屏显示到屏幕上。
- 监听鼠标点击和移动操作,将点击位置和光标当前位置作为矩形的对角。
- 根据光标当前位置显示确认按钮
- 在全屏截图上将这个矩形描边绘制,重新显示到屏幕上
- 点击确认后将选中区域裁剪出来存入剪贴板
通过tkinter定义窗口
import tkinter as tk
from PIL import ImageGrab, ImageTk
import image
class Window:
def __init__(self) -> None:
root = tk.Tk()
root.attributes('-topmost', True)
root.attributes('-fullscreen', True)
root.bind('', lambda _: root.destroy())
backgroud_frame = tk.Frame(root)
backgroud_frame.grid()
# 用于显示全屏截图
label = tk.Label(backgroud_frame, text='123')
label.grid()
label.bind('', self.onHoldLeftButton)
label.bind('', self.onHoldLeftButtonMove)
# 截图按钮
toolkit = tk.Frame(backgroud_frame)
tk.Button(toolkit, text='截图', command=self.save_to_clipboard).pack()
self.toolkit = toolkit
self.label = label
self.background_frame = backgroud_frame
self.root = root
self.start_x = 0
self.start_y = 0
self.end_x = 0
self.end_y = 0
def onHoldLeftButtonMove(self, event):
self.end_x, self.end_y = event.x, event.y
# 绘制矩形
self.draw_rectangle()
# 根据光标当前位置重新摆放截图按钮
self.show_toolkit()
def onHoldLeftButton(self, event):
self.start_x, self.start_y = event.x, event.y
def draw_rectangle(self):
pos = (self.start_x, self.start_y, self.end_x, self.end_y)
img = image.highlight_area(self.image, pos)
img = image.draw_rectangle(img, pos)
self.label_image =ImageTk.PhotoImage(img)
self.label.configure(image= self.label_image)
def show_toolkit(self):
self.toolkit.place(x=self.end_x, y=self.end_y, anchor='ne')
def save_to_clipboard(self):
pos = (self.start_x, self.start_y, self.end_x, self.end_y)
image.save_to_clipboard(image.get_area(self.image, pos))
self.root.destroy()
def show_forever(self):
self.image = ImageGrab.grab()
# 需要label_image保存引用,否则会被回收,导致无法显示
self.label_image =ImageTk.PhotoImage(self.image)
self.label.configure(image= self.label_image)
self.root.mainloop()
if __name__ == '__main__':
Window().show_forever()
封装图片操作
from PIL import Image, ImageGrab, ImageDraw
import numpy as np
import clipboard
def draw_rectangle(image: Image.Image, pos):
img = image.copy()
dImage = ImageDraw.Draw(img)
dImage.rectangle(pos, outline='red', width=2)
return img
def highlight_area(image: Image.Image, pos):
background = get_dark_iamge(image)
interest = get_area(image, pos)
background.paste(interest, pos)
return background
def get_area(image: Image.Image, pos):
return image.crop(pos)
def get_dark_iamge(image: Image.Image):
dark_arr = np.array(image) * 0.4
return Image.fromarray(dark_arr.astype('uint8'))
def save_to_clipboard(image: Image.Image):
image.convert('RGB')
image.save('temp.bmp', 'BMP')
clipboard.save_bitmap_to_clipboard('temp.bmp', image.width, image.height)
封装剪贴板操作
import ctypes
from ctypes.wintypes import HANDLE,HGLOBAL,UINT, LPVOID, BOOL,LPCSTR
def save_bitmap_to_clipboard(path, width, height):
CF_BITMAP = 2
user32 = ctypes.windll.user32
user32.OpenClipboard(None) # 获取剪贴板的锁,传NULL表示用当前进程获取
user32.EmptyClipboard()
alloc = loadBitMapInMem(path, width, height)
# 把内存空间地址传给剪贴板
setClipboardData(CF_BITMAP, alloc)
user32.CloseClipboard()
def loadBitMapInMem(path:str, width, height):
user32 = ctypes.windll.user32
h = user32.LoadImageA(None, LPCSTR(path.encode('utf8')), 0, width, height, 0x00000010)
return h
def setClipboardData(type, alloc):
user32 = ctypes.windll.user32
user32.SetClipboardData.argtypes = [UINT, HANDLE]
user32.SetClipboardData.restype = HANDLE
user32.SetClipboardData(type, ctypes.c_char_p(alloc))



