110 lines
3.4 KiB
Python
110 lines
3.4 KiB
Python
import tkinter as tk
|
|
from tkinter import filedialog
|
|
from PIL import Image, ImageTk
|
|
|
|
|
|
class ScreenshotSelector:
|
|
def __init__(self, parent, output_path, initial_path=None):
|
|
self.parent = parent
|
|
self.output_path = output_path
|
|
self.saved = False
|
|
self.start_x = None
|
|
self.start_y = None
|
|
self.rect = None
|
|
|
|
path = initial_path
|
|
if not path:
|
|
path = filedialog.askopenfilename(
|
|
parent=parent,
|
|
title='Choose a reference screenshot',
|
|
filetypes=[
|
|
('Images', '*.png *.jpg *.jpeg *.bmp *.webp'),
|
|
('All files', '*.*'),
|
|
],
|
|
)
|
|
if not path:
|
|
return
|
|
|
|
self.image = Image.open(path).convert('RGB')
|
|
max_w = max(300, parent.winfo_screenwidth() - 100)
|
|
max_h = max(300, parent.winfo_screenheight() - 160)
|
|
self.scale = min(1.0, max_w / self.image.width, max_h / self.image.height)
|
|
shown_size = (
|
|
max(1, round(self.image.width * self.scale)),
|
|
max(1, round(self.image.height * self.scale)),
|
|
)
|
|
self.shown = self.image.resize(shown_size, Image.Resampling.LANCZOS)
|
|
|
|
self.root = tk.Toplevel(parent)
|
|
self.root.title('Select the object or area to find')
|
|
self.root.attributes('-topmost', True)
|
|
self.root.protocol('WM_DELETE_WINDOW', self.root.destroy)
|
|
|
|
label = tk.Label(
|
|
self.root,
|
|
text='Drag a rectangle around the exact object/area to find',
|
|
padx=8,
|
|
pady=6,
|
|
)
|
|
label.pack(fill='x')
|
|
|
|
self.canvas = tk.Canvas(
|
|
self.root,
|
|
width=shown_size[0],
|
|
height=shown_size[1],
|
|
cursor='cross',
|
|
highlightthickness=0,
|
|
)
|
|
self.canvas.pack()
|
|
self.photo = ImageTk.PhotoImage(self.shown)
|
|
self.canvas.create_image(0, 0, image=self.photo, anchor='nw')
|
|
self.canvas.bind('<ButtonPress-1>', self.on_press)
|
|
self.canvas.bind('<B1-Motion>', self.on_drag)
|
|
self.canvas.bind('<ButtonRelease-1>', self.on_release)
|
|
|
|
self.root.grab_set()
|
|
self.parent.wait_window(self.root)
|
|
|
|
def on_press(self, event):
|
|
self.start_x, self.start_y = event.x, event.y
|
|
if self.rect is not None:
|
|
self.canvas.delete(self.rect)
|
|
self.rect = self.canvas.create_rectangle(
|
|
event.x,
|
|
event.y,
|
|
event.x,
|
|
event.y,
|
|
outline='#ff00ff',
|
|
width=3,
|
|
)
|
|
|
|
def on_drag(self, event):
|
|
if self.rect is not None:
|
|
self.canvas.coords(
|
|
self.rect,
|
|
self.start_x,
|
|
self.start_y,
|
|
event.x,
|
|
event.y,
|
|
)
|
|
|
|
def on_release(self, event):
|
|
x1, x2 = sorted((self.start_x, event.x))
|
|
y1, y2 = sorted((self.start_y, event.y))
|
|
if x2 - x1 < 10 or y2 - y1 < 10:
|
|
return
|
|
|
|
box = (
|
|
max(0, round(x1 / self.scale)),
|
|
max(0, round(y1 / self.scale)),
|
|
min(self.image.width, round(x2 / self.scale)),
|
|
min(self.image.height, round(y2 / self.scale)),
|
|
)
|
|
self.image.crop(box).save(self.output_path)
|
|
self.saved = True
|
|
self.root.destroy()
|
|
|
|
|
|
def select_from_screenshot(parent, output_path, initial_path=None):
|
|
return ScreenshotSelector(parent, output_path, initial_path).saved
|