import json import sys import os import time import cv2 import tkinter as tk import ctypes def enable_dpi_awareness(): """Keep screenshot pixels and mouse coordinates in the same coordinate system.""" try: ctypes.windll.shcore.SetProcessDpiAwareness(2) except Exception: try: ctypes.windll.user32.SetProcessDPIAware() except Exception: pass enable_dpi_awareness() os.chdir(os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(__file__)) from capture import CaptureOverlay from scanner import Scanner from actions import move_to from gui import LogWindow from screenshot_selector import select_from_screenshot def main(): log = None try: with open('config.json') as f: config = json.load(f) log = LogWindow() if not os.path.exists(config['template_path']): log.log('Choose a screenshot, then mark the area to find...') if not select_from_screenshot(log.root, config['template_path']): log.log('Selection cancelled.') return scanner = Scanner(config) log.log(f'Template loaded: {scanner.tw}x{scanner.th}') sys.stderr.write(f'[DEBUG] Scanner loaded template {scanner.tw}x{scanner.th}\n') recapture_flag = [False] clicked = False try: import keyboard keyboard.add_hotkey('ctrl+shift+r', lambda: recapture_flag.__setitem__(0, True)) keyboard.add_hotkey( 'ctrl+shift+s', lambda: setattr(log, 'screenshot_requested', True), ) log.log('Ctrl+Shift+S = choose from screenshot') log.log('Ctrl+Shift+R = capture from screen') sys.stderr.write('[DEBUG] hotkey registered\n') except Exception as e: log.log(f'Hotkey not available (run as admin?): {e}') sys.stderr.write(f'[DEBUG] hotkey failed: {e}\n') log.log('Scanning started') sys.stderr.write('[DEBUG] main loop started\n') cycle = 0 while log.running: if log.screenshot_requested: log.screenshot_requested = False log.log('Choose a screenshot and mark the exact target...') if select_from_screenshot(log.root, config['template_path']): scanner = Scanner(config) clicked = False log.log(f'New template loaded: {scanner.tw}x{scanner.th}') else: log.log('Selection cancelled; keeping the previous template.') continue if log.capture_requested or recapture_flag[0]: log.capture_requested = False recapture_flag[0] = False log.log('Recapture: select new area...') capture = CaptureOverlay(log.root, config['template_path']) if not capture.saved: log.log('Recapture cancelled; keeping the previous template.') continue scanner.template = cv2.imread(config['template_path']) if scanner.template is None: log.log('Error: new template not saved') else: scanner.th, scanner.tw = scanner.template.shape[:2] log.log('New template saved!') screen = scanner.grab_screen() result = scanner.find(screen) cycle += 1 if result and not clicked: x, y = result log.log(f'Match: ({x}, {y}) score={scanner.last_score:.3f}') sys.stderr.write(f'[MATCH] score={scanner.last_score:.3f}\n') move_to(x, y) log.log(f'Ctrl+clicked at ({x}, {y})') clicked = True elif not result: clicked = False if cycle % 10 == 0: sys.stderr.write(f'[SCAN] score={scanner.last_score:.3f} (need {config["confidence"]})\n') if scanner.last_score > 0.05: log.log(f'Best score: {scanner.last_score:.3f} (need {config["confidence"]})') if not log.running: break log.update() time.sleep(config['scan_interval_ms'] / 1000) except (KeyboardInterrupt, tk.TclError): pass except Exception as e: msg = f'[FATAL] {e}' sys.stderr.write(msg + '\n') if log is not None: try: log.log(msg) except Exception: pass raise finally: try: import keyboard keyboard.unhook_all_hotkeys() except Exception: pass if log is not None and log.running: try: log.log('Stopped') except Exception: pass if __name__ == '__main__': main()