File size: 4,964 Bytes
57f1366 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | import customtkinter as ctk
import tkinter as tk
import chess
import win32gui
import win32con
import win32api
class SelectionOverlay(ctk.CTkToplevel):
def __init__(self, master, on_select_callback):
super().__init__(master)
self.on_select_callback = on_select_callback
self.attributes("-alpha", 0.3)
self.attributes("-fullscreen", True)
self.attributes("-topmost", True)
self.configure(bg="black")
self.start_x = None
self.start_y = None
self.rect = None
self.canvas = ctk.CTkCanvas(self, cursor="cross", bg="grey15", highlightthickness=0)
self.canvas.pack(fill="both", expand=True)
self.canvas.bind("<ButtonPress-1>", self.on_press)
self.canvas.bind("<B1-Motion>", self.on_drag)
self.canvas.bind("<ButtonRelease-1>", self.on_release)
# Exit on Escape
self.bind("<Escape>", lambda e: self.destroy())
def on_press(self, event):
self.start_x = event.x
self.start_y = event.y
if self.rect:
self.canvas.delete(self.rect)
self.rect = self.canvas.create_rectangle(self.start_x, self.start_y, self.start_x, self.start_y, outline="red", width=2)
def on_drag(self, event):
cur_x, cur_y = (event.x, event.y)
self.canvas.coords(self.rect, self.start_x, self.start_y, cur_x, cur_y)
def on_release(self, event):
end_x, end_y = (event.x, event.y)
# Calculate region
left = min(self.start_x, end_x)
top = min(self.start_y, end_y)
width = abs(self.start_x - end_x)
height = abs(self.start_y - end_y)
if width > 50 and height > 50:
self.on_select_callback({'left': left, 'top': top, 'width': width, 'height': height})
self.destroy()
else:
# Too small, just reset
pass
class ProjectionOverlay(ctk.CTkToplevel):
def __init__(self, region):
"""
region: {'left': int, 'top': int, 'width': int, 'height': int}
"""
super().__init__()
self.region = region
# Position exactly over the board
self.geometry(f"{region['width']}x{region['height']}+{region['left']}+{region['top']}")
# Remove title bar
self.overrideredirect(True)
self.attributes("-topmost", True)
# Transparency setup for Windows
# We use a specific color as the transparent key.
self.transparent_color = "#000001" # Very nearly black
self.configure(bg=self.transparent_color)
self.wm_attributes("-transparentcolor", self.transparent_color)
# Canvas for drawing
self.canvas = tk.Canvas(self, bg=self.transparent_color, highlightthickness=0)
self.canvas.pack(fill="both", expand=True)
# Make click-through
self.make_click_through()
def make_click_through(self):
hwnd = win32gui.GetParent(self.winfo_id())
# If GetParent returns 0, try the window id directly (sometimes needed for Toplevels without parents)
if hwnd == 0:
hwnd = self.winfo_id()
styles = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
styles = styles | win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, styles)
def draw_best_move(self, move):
"""
Draw an arrow for the best move.
move: python-chess Move object (e.g. e2e4)
"""
self.canvas.delete("all")
if not move:
return
# Calculate coordinates
# File 0-7 (a-h), Rank 0-7 (1-8)
# 0,0 is bottom-left (a1)
sq_w = self.region['width'] / 8
sq_h = self.region['height'] / 8
start_sq = move.from_square
end_sq = move.to_square
# Convert to x,y (top-left origin for drawing)
# File (x) is same
# Rank (y) needs flip: Rank 0 is at bottom (y moves down) -> Rank 7 is top (y=0)
# Rank r -> visual row (7-r)
x1 = (chess.square_file(start_sq) + 0.5) * sq_w
y1 = ((7 - chess.square_rank(start_sq)) + 0.5) * sq_h
x2 = (chess.square_file(end_sq) + 0.5) * sq_w
y2 = ((7 - chess.square_rank(end_sq)) + 0.5) * sq_h
# Draw Arrow
self.canvas.create_line(x1, y1, x2, y2, fill="#00ff00", width=6, arrow=tk.LAST, arrowshape=(16, 20, 6), tag="arrow")
# Draw Circle on target
r = min(sq_w, sq_h) * 0.2
self.canvas.create_oval(x2-r, y2-r, x2+r, y2+r, outline="#00ff00", width=4, tag="arrow")
def clear(self):
self.canvas.delete("all")
|