| | 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)
|
| |
|
| |
|
| | 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)
|
| |
|
| |
|
| | 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:
|
| |
|
| | pass
|
| |
|
| | class ProjectionOverlay(ctk.CTkToplevel):
|
| | def __init__(self, region):
|
| | """
|
| | region: {'left': int, 'top': int, 'width': int, 'height': int}
|
| | """
|
| | super().__init__()
|
| |
|
| | self.region = region
|
| |
|
| | self.geometry(f"{region['width']}x{region['height']}+{region['left']}+{region['top']}")
|
| |
|
| |
|
| | self.overrideredirect(True)
|
| | self.attributes("-topmost", True)
|
| |
|
| |
|
| |
|
| | self.transparent_color = "#000001"
|
| | self.configure(bg=self.transparent_color)
|
| | self.wm_attributes("-transparentcolor", self.transparent_color)
|
| |
|
| |
|
| | self.canvas = tk.Canvas(self, bg=self.transparent_color, highlightthickness=0)
|
| | self.canvas.pack(fill="both", expand=True)
|
| |
|
| |
|
| | self.make_click_through()
|
| |
|
| | def make_click_through(self):
|
| | hwnd = win32gui.GetParent(self.winfo_id())
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | sq_w = self.region['width'] / 8
|
| | sq_h = self.region['height'] / 8
|
| |
|
| | start_sq = move.from_square
|
| | end_sq = move.to_square
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| | self.canvas.create_line(x1, y1, x2, y2, fill="#00ff00", width=6, arrow=tk.LAST, arrowshape=(16, 20, 6), tag="arrow")
|
| |
|
| |
|
| | 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")
|
| |
|