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('', self.on_press) self.canvas.bind('', self.on_drag) self.canvas.bind('', 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