424 lines
8.6 KiB
Python
424 lines
8.6 KiB
Python
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()
|