【python】利用PIL和tkinter制作的自定义大小的截图工具

分享一款使用Python和Tkinter实现的轻量级截图工具,无需依赖QQ,通过键盘快捷键画框截图并自动粘贴,适合实验室环境。

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

制作缘由

腾讯qq的快捷键截图实在是太方便了,但是每次截图都需要打开qq。打开qq是需要网络的,而且学校实验室中根本就没有qq!!(它自带的截图软件还总是有弹窗,麻烦!!)。
所以,就写了这么一个简易的截图软件(快捷键自己设定)

图片预览及用法

不多BB,先上成品展示
画一个框,截出框内图
就是画一个框,点击绿色的√,截出框内图片,点击红色的×,重新选择区域截图。或者再画一个方框也是可以达到重新选择区域截图的效果。(√和×是搜狗打出来的符号,不是图片,无额外资源,可放心食用)截图后图片自动传入剪贴板,按Ctrl+V即可在所需要的地方粘贴。

整体思路

  1. 利用tkinter创建一个覆盖全屏的透明窗口
  2. 在窗口上创建一个同样大小的画布
  3. 在画布上可自由画矩形
  4. 记录鼠标点击,移动,释放事件
  5. 关闭窗口
  6. 根据鼠标事件记录坐标,利用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()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值