From 45aa45764005125017f84b4db946dbea12f76837 Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 25 May 2026 23:40:39 +0000 Subject: [PATCH] Add helper.py --- helper.py | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 helper.py diff --git a/helper.py b/helper.py new file mode 100644 index 0000000..4a76606 --- /dev/null +++ b/helper.py @@ -0,0 +1,423 @@ +import ctypes +import tkinter as tk + + +try: + ctypes.windll.shcore.SetProcessDpiAwareness(2) + +except Exception: + pass + + +class GridOverlay: + def __init__( + self, + color="#00ff66", + line_width=2, + mode="thirds", + ): + self.root = tk.Tk() + + self.root.overrideredirect(True) + + self.root.attributes( + "-topmost", + True, + ) + + self.root.attributes( + "-alpha", + 0.99, + ) + + self.width = self.root.winfo_screenwidth() + + self.height = self.root.winfo_screenheight() + + self.root.geometry(f"{self.width}x{self.height}+0+0") + + self.transparent = "#000001" + + self.root.configure(bg=self.transparent) + + try: + self.root.wm_attributes( + "-transparentcolor", + self.transparent, + ) + + hwnd = ctypes.windll.user32.GetParent(self.root.winfo_id()) + + style = ctypes.windll.user32.GetWindowLongW( + hwnd, + -20, + ) + + style |= 0x80000 + style |= 0x20 + + ctypes.windll.user32.SetWindowLongW( + hwnd, + -20, + style, + ) + + except Exception: + pass + + self.canvas = tk.Canvas( + self.root, + width=self.width, + height=self.height, + bg=self.transparent, + highlightthickness=0, + bd=0, + ) + + self.canvas.pack( + fill="both", + expand=True, + ) + + self.color = color + self.line_width = line_width + self.mode = mode + self.visible = True + + self.items = {} + self.active_items = [] + + self.feedback_color = "#00ff66" + + self.feedback_text = None + self.direction_arrow = None + + self.build_modes() + + self.build_feedback() + + self.set_mode(mode) + + def build_feedback(self): + self.feedback_text = self.canvas.create_text( + self.width // 2, + 60, + text="", + fill="#00ff66", + font=( + "Segoe UI", + 22, + "bold", + ), + state="hidden", + ) + + self.direction_arrow = self.canvas.create_line( + 0, + 0, + 0, + 0, + fill="#00ff66", + width=5, + arrow=tk.LAST, + state="hidden", + ) + + def update_feedback( + self, + result, + ): + state = result["state"] + + if state == "green": + color = "#00ff66" + + elif state == "yellow": + color = "#ffee00" + + else: + color = "#ff3333" + + score = round(result["score"] * 100) + + self.canvas.itemconfigure( + self.feedback_text, + text=f"{state.upper()} {score}%", + fill=color, + state="normal", + ) + + direction = result["direction"] + + self.render_direction_arrow( + direction, + color, + ) + + def render_direction_arrow( + self, + direction, + color, + ): + cx = self.width // 2 + cy = self.height // 2 + + if direction == "move_left": + coords = ( + cx + 200, + cy, + cx - 200, + cy, + ) + + elif direction == "move_right": + coords = ( + cx - 200, + cy, + cx + 200, + cy, + ) + + elif direction == "reduce_headroom": + coords = ( + cx, + cy + 180, + cx, + cy - 180, + ) + + elif direction == "recenter_subject": + coords = ( + cx, + cy - 180, + cx, + cy + 180, + ) + + else: + self.canvas.itemconfigure( + self.direction_arrow, + state="hidden", + ) + + return + + self.canvas.coords( + self.direction_arrow, + *coords, + ) + + self.canvas.itemconfigure( + self.direction_arrow, + fill=color, + state="normal", + ) + + def schedule( + self, + callback, + ms, + ): + self.root.after( + ms, + callback, + ) + + def create_line( + self, + *coords, + ): + return self.canvas.create_line( + *coords, + fill=self.color, + width=self.line_width, + state="hidden", + ) + + def create_circle( + self, + x, + y, + radius, + fill="", + ): + return self.canvas.create_oval( + x - radius, + y - radius, + x + radius, + y + radius, + outline=self.color, + fill=fill, + width=self.line_width, + state="hidden", + ) + + def build_modes(self): + w = self.width + h = self.height + + cx = w // 2 + cy = h // 2 + + tx = w // 3 + ttx = (w // 3) * 2 + + ty = h // 3 + tty = (h // 3) * 2 + + self.items["thirds"] = [ + self.create_line( + tx, + 0, + tx, + h, + ), + self.create_line( + ttx, + 0, + ttx, + h, + ), + self.create_line( + 0, + ty, + w, + ty, + ), + self.create_line( + 0, + tty, + w, + tty, + ), + ] + + def hide_all(self): + for group in self.items.values(): + for item in group: + self.canvas.itemconfigure( + item, + state="hidden", + ) + + def set_mode( + self, + mode, + ): + if mode not in self.items: + mode = "thirds" + + self.mode = mode + + self.hide_all() + + self.active_items = self.items[mode] + + if self.visible: + for item in self.active_items: + self.canvas.itemconfigure( + item, + state="normal", + ) + + def toggle(self): + self.visible = not self.visible + + state = "normal" if self.visible else "hidden" + + for item in self.active_items: + self.canvas.itemconfigure( + item, + state=state, + ) + + def set_color( + self, + color, + ): + self.color = color + + for group in self.items.values(): + for item in group: + item_type = self.canvas.type(item) + + if item_type == "line": + self.canvas.itemconfigure( + item, + fill=color, + ) + + elif item_type == "oval": + current_fill = self.canvas.itemcget( + item, + "fill", + ) + + is_filled = ( + current_fill + and current_fill != "" + and current_fill != self.transparent + ) + + self.canvas.itemconfigure( + item, + outline=color, + fill=(color if is_filled else ""), + ) + + def resize_overlay(self): + width = self.root.winfo_screenwidth() + + height = self.root.winfo_screenheight() + + if width == self.width and height == self.height: + return + + self.width = width + self.height = height + + self.root.geometry(f"{width}x{height}+0+0") + + self.canvas.config( + width=width, + height=height, + ) + + self.canvas.delete("all") + + self.items.clear() + + self.active_items.clear() + + self.build_modes() + + self.build_feedback() + + self.set_mode(self.mode) + + def schedule_monitor_refresh( + self, + ): + self.resize_overlay() + + if self.visible: + self.schedule( + self.schedule_monitor_refresh, + 1000, + ) + + def destroy(self): + try: + self.visible = False + + self.canvas.delete("all") + + self.root.quit() + + self.root.destroy() + + except Exception: + pass + + def run(self): + self.root.mainloop()