pixel-hunter/main.py
2026-07-29 21:12:25 +03:00

149 lines
4.9 KiB
Python

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