import io import math import tkinter as tk import win32clipboard from PIL import Image, ImageTk, ImageChops, ImageDraw, ImageGrab import threading class PinPicture(tk.Frame): def __init__(self, image, master=None): super().__init__(master) self.lineBtn = None self.arrowBtn = None self.rectBtn = None self.penBtn = None self.drawModeBtn = None self.scale_factor = 1.0 self.photo = None self.canvas = None self.master = master self.pad = Image.new('RGBA', image.size) self.drawPad = ImageDraw.Draw(self.pad) self.pack() self.x = 0 self.y = 0 self.penX = 0 self.penY = 0 self.penX1 = 0 self.penY1 = 0 self.cutX = 0 self.cutY = 0 self.lastDraw = 0 self.drawing = False self.drawMode = False self.color = "red" self.image = image if image.mode != "RGBA": image = image.convert("RGBA") self.create_window() self.penWidth = 3 self.arrowSize = 10 # 创建窗口 def create_window(self): self.master.overrideredirect(True) self.master.attributes('-transparentcolor', 'white') self.master.config(bg='white') self.master.attributes('-topmost', True) self.canvas = tk.Canvas(self.master, height=self.image.height, width=self.image.width) self.penBtn = tk.Button(text='笔', command=self.set_pen) self.rectBtn = tk.Button(text='矩形', command=self.set_rect) self.arrowBtn = tk.Button(text='箭头', command=self.set_arrow) self.lineBtn = tk.Button(text='直线', command=self.set_line) self.master.bind("", self.close) self.master.bind("", self.enter_canvas) self.master.bind("", self.leave) self.photo = ImageTk.PhotoImage(Image.alpha_composite(self.image, self.pad)) self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo) self.canvas.pack(fill=tk.BOTH, expand=True, side=tk.LEFT) self.canvas.bind("", self.close) self.canvas.bind("", self.stop_draw) self.canvas.bind("", self.on_save) self.cut_mode_event() def on_save(self, event): output = io.BytesIO() self.image.convert("RGB").save(output, "BMP") data = output.getvalue()[14:] # BMP 文件头前14字节 output.close() win32clipboard.OpenClipboard() try: win32clipboard.EmptyClipboard() win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data) finally: win32clipboard.CloseClipboard() def cut_mode_event(self): self.canvas.bind("", self.on_cut_start) self.canvas.bind("", self.on_cut) self.canvas.bind("", self.on_copy) self.canvas.unbind("") self.pad = Image.new('RGBA', self.image.size, color=(0, 0, 0, 100)) self.drawPad = ImageDraw.Draw(self.pad) def on_copy(self, event): self.x = self.penX self.y = self.penY self.image = self.image.crop((self.penX, self.penY, self.cutX, self.cutY)) self.pad = Image.new('RGBA', self.image.size) self.drawPad = ImageDraw.Draw(self.pad) self.update_canvas() self.master.geometry(f"+{self.x}+{self.y}") self.pin_mode_event() self.canvas.bind("", self.close) def pin_mode_event(self): self.canvas.bind("", self.on_drag_start) self.canvas.bind("", self.on_drag) self.canvas.bind("", self.on_mouse_wheel) # 鼠标进入 def enter_canvas(self, event): self.canvas.focus_set() if self.drawModeBtn is None and not self.drawMode: self.drawModeBtn = tk.Button(text="编辑", command=self.draw_mode_event) self.drawModeBtn.pack(side=tk.TOP, expand=True) elif not self.drawMode: self.drawModeBtn.pack(side=tk.TOP, expand=True) # 鼠标离开 def leave(self, event): if self.drawModeBtn is not None and not self.drawMode: self.drawModeBtn.pack_forget() # 进入编辑模式 def draw_mode_event(self): self.drawMode = True self.drawModeBtn.pack_forget() self.canvas.bind("", self.pen) self.penBtn.pack() self.rectBtn.pack() self.arrowBtn.pack() self.lineBtn.pack() def set_pen(self): self.canvas.bind("", self.pen) def set_rect(self): self.canvas.bind("", self.rect) def set_arrow(self): self.canvas.bind("", self.arrow) def set_line(self): self.canvas.bind("", self.line) # 拖动图片设置窗口坐标 def on_drag(self, event): delta_x = event.x - self.x delta_y = event.y - self.y new_x = self.master.winfo_x() + delta_x new_y = self.master.winfo_y() + delta_y self.master.geometry(f"+{new_x}+{new_y}") def on_cut(self, event): self.start_draw(event) self.pad = Image.new('RGBA', self.image.size, color=(0, 0, 0, 100)) self.drawPad = ImageDraw.Draw(self.pad) self.drawPad.rectangle((self.penX, self.penY, event.x, event.y), fill=(0,0,0,0), outline="purple") self.update_canvas_cut() self.cutX = event.x self.cutY = event.y # 记录开始拖动的坐标 def on_drag_start(self, event): self.x = event.x self.y = event.y def on_cut_start(self, event): self.pad = Image.new('RGBA', self.image.size, color=(0, 0, 0, 100)) self.drawPad = ImageDraw.Draw(self.pad) self.penX = event.x self.penY = event.y # 关闭窗口 def close(self, event): self.master.destroy() # 滚轮调整大小 def on_mouse_wheel(self, event): if self.drawMode: if event.delta > 0: self.penWidth += 1 self.arrowSize += 1 else: self.penWidth -= 1 self.arrowSize -= 1 else: if event.delta > 0: self.scale_factor *= 1.1 else: self.scale_factor /= 1.1 self.update_canvas() def update_canvas(self): new_width = int(self.image.width * self.scale_factor) new_height = int(self.image.height * self.scale_factor) image = Image.alpha_composite(self.image, self.pad) image = image.resize((new_width, new_height)) self.photo = ImageTk.PhotoImage(image) self.canvas.config(width=new_width, height=new_height) self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo) def update_canvas_cut(self): new_width = int(self.image.width * self.scale_factor) new_height = int(self.image.height * self.scale_factor) image1 = Image.alpha_composite(self.image, self.pad) image1 = image1.resize((new_width, new_height)) self.photo = ImageTk.PhotoImage(image1) self.canvas.config(width=new_width, height=new_height) self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo) def start_draw(self, event): if not self.drawing: self.drawing = True self.penX = event.x self.penY = event.y self.penY1 = event.y self.penX1 = event.x def pen(self, event): self.start_draw(event) self.drawPad.line(self.point_convert(self.penX, self.penY, event.x, event.y), self.color, width=self.penWidth) self.drawPad.line(self.point_convert(self.penX1, self.penY1, event.x, event.y), self.color, width=self.penWidth) self.penX1 = self.penX self.penY1 = self.penY self.penX = event.x self.penY = event.y self.update_canvas() def line(self, event): self.start_draw(event) self.pad = Image.new('RGBA', self.image.size) self.drawPad = ImageDraw.Draw(self.pad) self.drawPad.line(self.point_convert(self.penX, self.penY, event.x, event.y), self.color, width=self.penWidth) self.update_canvas() def rect(self, event): self.start_draw(event) self.pad = Image.new('RGBA', self.image.size) self.drawPad = ImageDraw.Draw(self.pad) self.drawPad.line(self.point_convert(self.penX, self.penY, self.penX, event.y), self.color, width=self.penWidth) self.drawPad.line(self.point_convert(self.penX, self.penY, event.x, self.penY), self.color, width=self.penWidth) self.drawPad.line(self.point_convert(event.x, self.penY, event.x, event.y), self.color, width=self.penWidth) self.drawPad.line(self.point_convert(self.penX, event.y, event.x, event.y), self.color, width=self.penWidth) self.update_canvas() def arrow(self, event): self.start_draw(event) self.pad = Image.new('RGBA', self.image.size) self.drawPad = ImageDraw.Draw(self.pad) pos = self.point_convert(self.penX, self.penY, event.x, event.y) self.drawPad.line(pos, self.color, width=self.penWidth) x1 = pos[0] y1 = pos[1] x2 = pos[2] y2 = pos[3] l = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) cosa = (x2 - x1) / l sina = (y2 - y1) / l k = sina / cosa d = self.arrowSize * (sina + cosa) if (sina < cosa): xa = x2 - d ya = y2 + math.sqrt(2 * self.arrowSize * self.arrowSize - d * d) xb = x2 - math.sqrt(2 * self.arrowSize * self.arrowSize - d * d) yb = y2 - d else: xa = x2 - d ya = y2 - math.sqrt(2 * self.arrowSize * self.arrowSize - d * d) xb = x2 + math.sqrt(2 * self.arrowSize * self.arrowSize - d * d) yb = y2 - d self.drawPad.polygon( ((xa, ya), (xb, yb), (pos[2], pos[3])), fill=self.color, outline=(0, 0, 0, 0)) self.update_canvas() def point_convert(self, x, y, x1, y1): real_x = int(x / self.scale_factor) real_y = int(y / self.scale_factor) real_x1 = int(x1 / self.scale_factor) real_y1 = int(y1 / self.scale_factor) return real_x, real_y, real_x1, real_y1 def stop_draw(self, event): self.drawing = False if self.drawMode: self.image = Image.alpha_composite(self.image, self.pad) self.pad = Image.new('RGBA', self.image.size) self.drawPad = ImageDraw.Draw(self.pad)