""" EU-Utility - OCR Scanner Plugin Reads text from in-game menus using OCR. """ import subprocess import platform import tempfile from pathlib import Path from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit, QComboBox, QFrame, QScrollArea, QGroupBox ) from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer from PyQt6.QtGui import QPixmap, QImage from plugins.base_plugin import BasePlugin class OCRScannerThread(QThread): """Background thread for OCR scanning.""" result_ready = pyqtSignal(str) error_occurred = pyqtSignal(str) def __init__(self, region=None): super().__init__() self.region = region # (x, y, width, height) def run(self): """Capture screen and perform OCR.""" try: system = platform.system() # Create temp file for screenshot with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp: screenshot_path = tmp.name # Capture screenshot if system == "Windows": # Use PowerShell to capture screen ps_cmd = f''' Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds $bitmap = New-Object System.Drawing.Bitmap($screen.Width, $screen.Height) $graphics = [System.Drawing.Graphics]::FromImage($bitmap) $graphics.CopyFromScreen($screen.Location, [System.Drawing.Point]::Empty, $screen.Size) $bitmap.Save("{screenshot_path}") $graphics.Dispose() $bitmap.Dispose() ''' subprocess.run(['powershell', '-Command', ps_cmd], capture_output=True, timeout=10) elif system == "Linux": # Use gnome-screenshot or import try: subprocess.run(['gnome-screenshot', '-f', screenshot_path], capture_output=True, timeout=10) except: subprocess.run(['import', '-window', 'root', screenshot_path], capture_output=True, timeout=10) # Perform OCR text = self._perform_ocr(screenshot_path) # Clean up Path(screenshot_path).unlink(missing_ok=True) self.result_ready.emit(text) except Exception as e: self.error_occurred.emit(str(e)) def _perform_ocr(self, image_path): """Perform OCR on image.""" try: # Try easyocr first import easyocr reader = easyocr.Reader(['en']) results = reader.readtext(image_path) text = '\n'.join([result[1] for result in results]) return text if text else "No text detected" except: pass try: # Try pytesseract import pytesseract from PIL import Image image = Image.open(image_path) text = pytesseract.image_to_string(image) return text if text.strip() else "No text detected" except: pass return "OCR not available. Install: pip install easyocr or pytesseract" class GameReaderPlugin(BasePlugin): """Read in-game menus and text using OCR.""" name = "Game Reader" version = "1.0.0" author = "ImpulsiveFPS" description = "OCR scanner for in-game menus and text" hotkey = "ctrl+shift+r" # R for Read def initialize(self): """Setup game reader.""" self.scan_thread = None self.last_result = "" def get_ui(self): """Create game reader UI.""" widget = QWidget() widget.setStyleSheet("background: transparent;") layout = QVBoxLayout(widget) layout.setSpacing(15) layout.setContentsMargins(0, 0, 0, 0) # Title title = QLabel("📷 Game Reader (OCR)") title.setStyleSheet("color: white; font-size: 16px; font-weight: bold;") layout.addWidget(title) # Info info = QLabel("Capture in-game menus and read the text") info.setStyleSheet("color: rgba(255, 255, 255, 150); font-size: 12px;") layout.addWidget(info) # Scan button scan_btn = QPushButton("Capture Screen") scan_btn.setStyleSheet(""" QPushButton { background-color: #4a9eff; color: white; padding: 15px; border: none; border-radius: 10px; font-size: 14px; font-weight: bold; } QPushButton:hover { background-color: #5aafff; } QPushButton:pressed { background-color: #3a8eef; } """) scan_btn.clicked.connect(self._capture_screen) layout.addWidget(scan_btn) # Status self.status_label = QLabel("Ready to capture") self.status_label.setStyleSheet("color: #666; font-size: 11px;") self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.status_label) # Results area results_frame = QFrame() results_frame.setStyleSheet(""" QFrame { background-color: rgba(0, 0, 0, 50); border-radius: 10px; border: 1px solid rgba(255, 255, 255, 20); } """) results_layout = QVBoxLayout(results_frame) results_layout.setContentsMargins(10, 10, 10, 10) results_label = QLabel("Captured Text:") results_label.setStyleSheet("color: rgba(255, 255, 255, 150); font-size: 12px;") results_layout.addWidget(results_label) self.result_text = QTextEdit() self.result_text.setPlaceholderText("Captured text will appear here...") self.result_text.setStyleSheet(""" QTextEdit { background-color: rgba(30, 30, 30, 100); color: white; border: none; border-radius: 6px; padding: 8px; font-size: 13px; } """) self.result_text.setMaximumHeight(150) results_layout.addWidget(self.result_text) # Copy button copy_btn = QPushButton("Copy Text") copy_btn.setStyleSheet(""" QPushButton { background-color: rgba(255, 255, 255, 20); color: white; padding: 8px; border: none; border-radius: 6px; } QPushButton:hover { background-color: rgba(255, 255, 255, 30); } """) copy_btn.clicked.connect(self._copy_text) results_layout.addWidget(copy_btn) layout.addWidget(results_frame) # Common uses uses_label = QLabel("Common Uses:") uses_label.setStyleSheet("color: rgba(255, 255, 255, 150); font-size: 11px; margin-top: 10px;") layout.addWidget(uses_label) uses_text = QLabel( "• Read NPC dialogue\n" "• Capture mission text\n" "• Extract item stats\n" "• Read shop prices" ) uses_text.setStyleSheet("color: rgba(255, 255, 255, 100); font-size: 11px;") layout.addWidget(uses_text) layout.addStretch() return widget def _capture_screen(self): """Capture screen and perform OCR.""" self.status_label.setText("Capturing...") self.status_label.setStyleSheet("color: #4a9eff;") # Start scan thread self.scan_thread = OCRScannerThread() self.scan_thread.result_ready.connect(self._on_result) self.scan_thread.error_occurred.connect(self._on_error) self.scan_thread.start() def _on_result(self, text): """Handle OCR result.""" self.result_text.setText(text) self.last_result = text self.status_label.setText(f"✅ Captured {len(text)} characters") self.status_label.setStyleSheet("color: #4caf50;") def _on_error(self, error): """Handle OCR error.""" self.status_label.setText(f"❌ Error: {error}") self.status_label.setStyleSheet("color: #f44336;") def _copy_text(self): """Copy text to clipboard.""" from PyQt6.QtWidgets import QApplication clipboard = QApplication.clipboard() clipboard.setText(self.result_text.toPlainText()) self.status_label.setText("📋 Copied to clipboard!") def on_hotkey(self): """Capture on hotkey.""" self._capture_screen()