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()