制作缘由
腾讯qq的快捷键截图实在是太方便了,但是每次截图都需要打开qq。打开qq是需要网络的,而且学校实验室中根本就没有qq!!(它自带的截图软件还总是有弹窗,麻烦!!)。
所以,就写了这么一个简易的截图软件(快捷键自己设定)
图片预览及用法
不多BB,先上成品展示
就是画一个框,点击绿色的√,截出框内图片,点击红色的×,重新选择区域截图。或者再画一个方框也是可以达到重新选择区域截图的效果。(√和×是搜狗打出来的符号,不是图片,无额外资源,可放心食用)截图后图片自动传入剪贴板,按Ctrl+V即可在所需要的地方粘贴。
整体思路
- 利用tkinter创建一个覆盖全屏的透明窗口
- 在窗口上创建一个同样大小的画布
- 在画布上可自由画矩形
- 记录鼠标点击,移动,释放事件
- 关闭窗口
- 根据鼠标事件记录坐标,利用PIL.ImageGrab.grab进行截图
代码展示
写完后只是粗略地测试了一下,可能会有一点bug。如果有bug及代码修改方式希望能告知一下,谢谢!!!!
from PIL import Image, ImageGrab
from tkinter import Tk, Button, Canvas
from ctypes import windll
from win32clipboard import OpenClipboard, EmptyClipboard, SetClipboardData, CloseClipboard
from win32con import CF_DIB
from io import BytesIO
def send_to_clipboard(clip_type, data):#将数据传入剪贴板
OpenClipboard()
EmptyClipboard()
SetClipboardData(clip_type, data)
CloseClipboard()
class CTkPrScrn:
def __init__(self):
self.start_x, self.start_y = 0, 0
self.end_x, self.end_y = 0, 0
self.on_x, self.on_y = 0, 0
self.scale = 1
self.WIN = Tk()
self.WIN.attributes("-alpha", 0.5) # 设置窗口半透明
self.WIN.attributes("-fullscreen", True) # 设置全屏
self.WIN.attributes("-topmost", True) # 设置窗口在最上层
self.width, self.height = self.WIN.winfo_screenwidth(), self.WIN.winfo_screenheight()
# 创建画布
self.canvas = Canvas(self.WIN, width=self.width, height=self.height, bg="gray")
self.WIN.bind('<Button-1>', self.xFunc1) # 绑定鼠标左键点击事件
self.WIN.bind('<ButtonRelease-1>', self.xFunc1) # 绑定鼠标左键点击释放事件
self.WIN.bind('<B1-Motion>', self.xFunc2) # 绑定鼠标左键点击移动事件
self.WIN.bind('<Escape>', lambda e: self.WIN.destroy()) # 绑定Esc按键退出事件
self.button_yes = Button(self.WIN, text='√', fg='green',command=self.event_yes)#确认截图按钮
self.button_no = Button(self.WIN, text='×', fg='red',command=self.event_no)#重新截图按钮
#self.button_yes.bind('<Button-1>',self.screenGrab)
#self.button_no.bind('<Button-1>',self.canvas.delete("prscrn"))
user32 = windll.user32
gdi32 = windll.gdi32
dc = user32.GetDC(None)
widthScale = gdi32.GetDeviceCaps(dc, 8) # 分辨率缩放后的宽度
heightScale = gdi32.GetDeviceCaps(dc, 10) # 分辨率缩放后的高度
width = gdi32.GetDeviceCaps(dc, 118) # 原始分辨率的宽度
height = gdi32.GetDeviceCaps(dc, 117) # 原始分辨率的高度
self.scale = width / widthScale
#print(self.width, self.height, widthScale, heightScale, width, height, self.scale)
self.WIN.mainloop() # 窗口持久化
def xFunc1(self, event):
# print(f"鼠标左键点击了一次坐标是:x={g_scale * event.x}, y={g_scale * event.y}")
#print(event.state)
if event.state == 8: # 鼠标左键按下
#print(event.x,event.y)
self.on_x, self.on_y = event.x, event.y
elif event.state == 264: # 鼠标左键释放
#print(event.x, event.y)
if event.x == self.start_x or event.y == self.start_y:
return
else:
self.end_x, self.end_y = event.x, event.y
self.button_yes.pack()
self.button_no.pack()
#print(self.end_y,self.height)
if self.end_y >= self.height - 100:
self.button_yes.place(x=self.end_x - 25, y=self.end_y - 35)
self.button_no.place(x=self.end_x - 50, y=self.end_y - 35)
else:
self.button_yes.place(x=self.end_x - 20, y=self.end_y + 5)
self.button_no.place(x=self.end_x - 45, y=self.end_y + 5)
def xFunc2(self, event):
# print(f"鼠标左键点击了一次坐标是:x={self.scale * event.x}, y={self.scale * event.y}")
self.start_x, self.start_y = self.on_x, self.on_y
if event.x == self.start_x or event.y == self.start_y:
return
self.canvas.delete("prscrn")
self.canvas.create_rectangle(self.start_x, self.start_y, event.x, event.y,
fill='white', outline='red', tag="prscrn")
# 包装画布
self.canvas.pack()
def event_yes(self):
self.WIN.update()
self.WIN.destroy()
im = ImageGrab.grab((self.scale * self.start_x, self.scale * self.start_y,
self.scale * self.end_x, self.scale * self.end_y))
#print(self.start_x,self.end_x)
# imgName = 'tmp.png'
#im.show()
output = BytesIO()
im.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()
send_to_clipboard(CF_DIB, data)
def event_no(self):
self.canvas.delete("prscrn")
self.button_yes.place_forget()
self.button_no.place_forget()
self.button_yes.pack_forget()
self.button_no.pack_forget()
if __name__ == '__main__':
prScrn = CTkPrScrn()