Files
CGW-Quote-Builder/app/region_helper.py
T
2026-04-11 00:04:09 -05:00

190 lines
6.8 KiB
Python

"""
Interactive Region Coordinate Helper
Opens an image and helps you find pixel coordinates for colorable regions.
Click on the image to see coordinates.
Usage:
python region_helper.py images/cobrai.jpg
"""
import sys
from PIL import Image, ImageDraw, ImageFont
import tkinter as tk
from tkinter import Canvas
from PIL import ImageTk
class RegionHelper:
def __init__(self, image_path):
self.image_path = image_path
self.image = Image.open(image_path)
self.width, self.height = self.image.size
# Setup UI
self.root = tk.Tk()
self.root.title(f"Region Helper - {image_path}")
# Scale image if too large
self.scale = 1.0
max_display_width = 1000
max_display_height = 800
if self.width > max_display_width or self.height > max_display_height:
scale_w = max_display_width / self.width
scale_h = max_display_height / self.height
self.scale = min(scale_w, scale_h)
new_width = int(self.width * self.scale)
new_height = int(self.height * self.scale)
self.display_image = self.image.resize((new_width, new_height), Image.LANCZOS)
else:
self.display_image = self.image.copy()
# Setup canvas
self.canvas = Canvas(
self.root,
width=self.display_image.width,
height=self.display_image.height
)
self.canvas.pack(side=tk.LEFT)
# Display image
self.photo = ImageTk.PhotoImage(self.display_image)
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo)
# Info panel
info_frame = tk.Frame(self.root, width=300)
info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, padx=10, pady=10)
tk.Label(info_frame, text="Image Dimensions:", font=('Arial', 12, 'bold')).pack(anchor=tk.W, pady=5)
tk.Label(info_frame, text=f"{self.width} x {self.height} pixels").pack(anchor=tk.W)
if self.scale != 1.0:
tk.Label(info_frame, text=f"Scaled: {self.scale:.2%}").pack(anchor=tk.W)
tk.Label(info_frame, text="\nClick to get coordinates:", font=('Arial', 12, 'bold')).pack(anchor=tk.W, pady=(20, 5))
self.coord_label = tk.Label(info_frame, text="X: -, Y: -", font=('Arial', 14))
self.coord_label.pack(anchor=tk.W, pady=5)
tk.Label(info_frame, text="\nFirst click = Top-Left", fg='blue').pack(anchor=tk.W)
tk.Label(info_frame, text="Second click = Bottom-Right", fg='blue').pack(anchor=tk.W)
tk.Label(info_frame, text="\nRegion JSON:", font=('Arial', 12, 'bold')).pack(anchor=tk.W, pady=(20, 5))
self.json_text = tk.Text(info_frame, height=15, width=35, font=('Courier', 9))
self.json_text.pack(fill=tk.BOTH, expand=True)
tk.Button(info_frame, text="Clear Points", command=self.clear_points).pack(pady=10)
tk.Button(info_frame, text="Copy JSON", command=self.copy_json).pack()
# Bind events
self.canvas.bind('<Motion>', self.on_mouse_move)
self.canvas.bind('<Button-1>', self.on_click)
# Store points
self.points = []
self.point_ids = []
self.update_json()
def on_mouse_move(self, event):
"""Show current mouse coordinates."""
x = int(event.x / self.scale)
y = int(event.y / self.scale)
self.coord_label.config(text=f"X: {x}, Y: {y}")
def on_click(self, event):
"""Record clicked point."""
x = int(event.x / self.scale)
y = int(event.y / self.scale)
# Limit to 2 points (top-left, bottom-right)
if len(self.points) >= 2:
self.clear_points()
self.points.append((x, y))
# Draw point on canvas
color = 'blue' if len(self.points) == 1 else 'red'
point_id = self.canvas.create_oval(
event.x - 5, event.y - 5,
event.x + 5, event.y + 5,
fill=color, outline='white', width=2
)
self.point_ids.append(point_id)
# Draw rectangle if we have both points
if len(self.points) == 2:
x1, y1 = self.points[0]
x2, y2 = self.points[1]
# Draw on canvas (scaled)
rect_id = self.canvas.create_rectangle(
x1 * self.scale, y1 * self.scale,
x2 * self.scale, y2 * self.scale,
outline='green', width=2, dash=(5, 5)
)
self.point_ids.append(rect_id)
self.update_json()
def clear_points(self):
"""Clear all points and rectangles."""
for point_id in self.point_ids:
self.canvas.delete(point_id)
self.points = []
self.point_ids = []
self.update_json()
def update_json(self):
"""Update the JSON display."""
self.json_text.delete('1.0', tk.END)
if len(self.points) == 0:
self.json_text.insert('1.0', '{\n "name": "region_name",\n "topLeft": [?, ?],\n "bottomRight": [?, ?],\n "description": "..."\n}')
elif len(self.points) == 1:
x, y = self.points[0]
self.json_text.insert('1.0', f'{{\n "name": "region_name",\n "topLeft": [{x}, {y}],\n "bottomRight": [?, ?],\n "description": "..."\n}}')
else:
x1, y1 = self.points[0]
x2, y2 = self.points[1]
# Calculate dimensions
width = abs(x2 - x1)
height = abs(y2 - y1)
json_str = f'''{{\n "name": "region_name",\n "topLeft": [{x1}, {y1}],\n "bottomRight": [{x2}, {y2}],\n "description": "Width: {width}px, Height: {height}px"\n}}'''
self.json_text.insert('1.0', json_str)
def copy_json(self):
"""Copy JSON to clipboard."""
json_str = self.json_text.get('1.0', tk.END).strip()
self.root.clipboard_clear()
self.root.clipboard_append(json_str)
self.root.update()
def run(self):
"""Start the GUI."""
self.root.mainloop()
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python region_helper.py <image_path>")
print("Example: python region_helper.py images/cobrai.jpg")
sys.exit(1)
image_path = sys.argv[1]
try:
helper = RegionHelper(image_path)
helper.run()
except FileNotFoundError:
print(f"Error: Image file not found: {image_path}")
sys.exit(1)
except Exception as e:
print(f"Error: {e}")
sys.exit(1)