283 lines
10 KiB
Python
283 lines
10 KiB
Python
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("<Escape>", self.close)
|
|
self.master.bind("<Enter>", self.enter_canvas)
|
|
self.master.bind("<Leave>", 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("<ButtonPress-3>", self.close)
|
|
self.canvas.bind("<ButtonRelease-1>", self.stop_draw)
|
|
self.canvas.bind("<Return>", 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("<ButtonPress-1>", self.on_cut_start)
|
|
self.canvas.bind("<B1-Motion>", self.on_cut)
|
|
self.canvas.bind("<ButtonPress-3>", self.on_copy)
|
|
self.canvas.unbind("<MouseWheel>")
|
|
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("<ButtonPress-3>", self.close)
|
|
|
|
def pin_mode_event(self):
|
|
self.canvas.bind("<ButtonPress-1>", self.on_drag_start)
|
|
self.canvas.bind("<B1-Motion>", self.on_drag)
|
|
self.canvas.bind("<MouseWheel>", 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("<B1-Motion>", self.pen)
|
|
self.penBtn.pack()
|
|
self.rectBtn.pack()
|
|
self.arrowBtn.pack()
|
|
self.lineBtn.pack()
|
|
|
|
def set_pen(self):
|
|
self.canvas.bind("<B1-Motion>", self.pen)
|
|
|
|
def set_rect(self):
|
|
self.canvas.bind("<B1-Motion>", self.rect)
|
|
|
|
def set_arrow(self):
|
|
self.canvas.bind("<B1-Motion>", self.arrow)
|
|
|
|
def set_line(self):
|
|
self.canvas.bind("<B1-Motion>", 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) |