""" EU-Utility - Game Reader Test Plugin Debug and test tool for OCR and game reading functionality. Tests screen capture, OCR accuracy, and text extraction. """ from pathlib import Path from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QLabel, QPushButton, QComboBox, QCheckBox, QSpinBox, QGroupBox, QSplitter, QFrame, QTabWidget, QLineEdit, QProgressBar, QFileDialog, QMessageBox ) from PyQt6.QtCore import Qt, QTimer, QThread, pyqtSignal from PyQt6.QtGui import QPixmap, QImage, QColor from plugins.base_plugin import BasePlugin class OCRTestThread(QThread): """Background thread for OCR testing.""" result_ready = pyqtSignal(dict) progress_update = pyqtSignal(int, str) def __init__(self, region=None, backend='auto'): super().__init__() self.region = region self.backend = backend def run(self): """Run OCR test.""" import time results = { 'success': False, 'text': '', 'backend_used': '', 'processing_time': 0, 'error': None } try: start_time = time.time() self.progress_update.emit(10, "Capturing screen...") # Capture screen from PIL import Image, ImageGrab if self.region: screenshot = ImageGrab.grab(bbox=self.region) else: screenshot = ImageGrab.grab() self.progress_update.emit(30, "Running OCR...") # Try OCR backends text = "" backend_used = "none" if self.backend in ('auto', 'easyocr'): try: import easyocr import numpy as np self.progress_update.emit(50, "Loading EasyOCR...") reader = easyocr.Reader(['en'], gpu=False, verbose=False) self.progress_update.emit(70, "Processing with EasyOCR...") # Convert PIL Image to numpy array screenshot_np = np.array(screenshot) ocr_result = reader.readtext( screenshot_np, detail=0, paragraph=True ) text = '\n'.join(ocr_result) backend_used = "easyocr" except Exception as e: if self.backend == 'easyocr': raise e if not text and self.backend in ('auto', 'tesseract'): try: import pytesseract self.progress_update.emit(70, "Processing with Tesseract...") text = pytesseract.image_to_string(screenshot) backend_used = "tesseract" except Exception as e: if self.backend == 'tesseract': raise e if not text and self.backend in ('auto', 'paddle'): try: from paddleocr import PaddleOCR import numpy as np self.progress_update.emit(70, "Processing with PaddleOCR...") # Try with show_log, fall back without for compatibility try: ocr = PaddleOCR(use_angle_cls=True, lang='en', show_log=False) except TypeError: ocr = PaddleOCR(use_angle_cls=True, lang='en') # Convert PIL to numpy screenshot_np = np.array(screenshot) result = ocr.ocr(screenshot_np, cls=True) if result and result[0]: texts = [line[1][0] for line in result[0]] text = '\n'.join(texts) backend_used = "paddleocr" except Exception as e: if self.backend == 'paddle': raise e processing_time = time.time() - start_time results.update({ 'success': True, 'text': text or "No text detected", 'backend_used': backend_used, 'processing_time': processing_time }) self.progress_update.emit(100, "Complete!") except Exception as e: results['error'] = str(e) self.progress_update.emit(100, f"Error: {e}") self.result_ready.emit(results) class GameReaderTestPlugin(BasePlugin): """Test and debug tool for game reading/OCR functionality.""" name = "Game Reader Test" version = "1.0.0" author = "EU-Utility" description = "Debug tool for testing OCR and screen reading" # Dependencies for OCR functionality dependencies = { 'pip': ['pillow', 'numpy'], 'optional': { 'easyocr': 'Best OCR accuracy, auto-downloads models', 'pytesseract': 'Alternative OCR engine', 'paddleocr': 'Advanced OCR with layout detection' } } def __init__(self, overlay_window, config): super().__init__(overlay_window, config) self.test_history = [] self.max_history = 50 self.ocr_thread = None def initialize(self): """Initialize plugin.""" self.log_info("Game Reader Test initialized") def get_ui(self): """Create plugin UI.""" widget = QWidget() layout = QVBoxLayout(widget) layout.setSpacing(12) # Header header = QLabel("šŸ“· Game Reader Test & Debug Tool") header.setStyleSheet("font-size: 16px; font-weight: bold; color: #ff8c42;") layout.addWidget(header) # Create tabs tabs = QTabWidget() # Tab 1: Quick Test tabs.addTab(self._create_quick_test_tab(), "Quick Test") # Tab 2: File Test (NEW) tabs.addTab(self._create_file_test_tab(), "File Test") # Tab 3: Region Test tabs.addTab(self._create_region_test_tab(), "Region Test") # Tab 3: History tabs.addTab(self._create_history_tab(), "History") # Tab 4: Calibration tabs.addTab(self._create_calibration_tab(), "Calibration") layout.addWidget(tabs, 1) # Status bar status_frame = QFrame() status_frame.setStyleSheet("background-color: #1a1f2e; border-radius: 6px; padding: 8px;") status_layout = QHBoxLayout(status_frame) self.status_label = QLabel("Ready - Select a tab to begin testing") self.status_label.setStyleSheet("color: #4ecdc4;") status_layout.addWidget(self.status_label) layout.addWidget(status_frame) return widget def _create_quick_test_tab(self): """Create quick test tab.""" tab = QWidget() layout = QVBoxLayout(tab) # Info info = QLabel("Quick OCR Test - Captures full screen and extracts text") info.setStyleSheet("color: #888;") layout.addWidget(info) # Backend selection backend_layout = QHBoxLayout() backend_layout.addWidget(QLabel("OCR Backend:")) self.backend_combo = QComboBox() self.backend_combo.addItems(["Auto (try all)", "EasyOCR", "Tesseract", "PaddleOCR"]) backend_layout.addWidget(self.backend_combo) backend_layout.addStretch() layout.addLayout(backend_layout) # Progress bar self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) self.progress_bar.setStyleSheet(""" QProgressBar { border: 1px solid #333; border-radius: 4px; text-align: center; } QProgressBar::chunk { background-color: #ff8c42; } """) layout.addWidget(self.progress_bar) # Test button self.test_btn = QPushButton("ā–¶ Run OCR Test") self.test_btn.setStyleSheet(""" QPushButton { background-color: #ff8c42; color: #141f23; font-weight: bold; padding: 12px; font-size: 14px; } QPushButton:hover { background-color: #ffa05c; } """) self.test_btn.clicked.connect(self._run_quick_test) layout.addWidget(self.test_btn) # Results results_group = QGroupBox("OCR Results") results_layout = QVBoxLayout(results_group) self.results_text = QTextEdit() self.results_text.setReadOnly(True) self.results_text.setPlaceholderText("OCR results will appear here...") self.results_text.setStyleSheet(""" QTextEdit { background-color: #0d1117; color: #c9d1d9; font-family: Consolas, monospace; font-size: 12px; } """) results_layout.addWidget(self.results_text) # Stats self.stats_label = QLabel("Backend: - | Time: - | Status: Waiting") self.stats_label.setStyleSheet("color: #888;") results_layout.addWidget(self.stats_label) layout.addWidget(results_group) # Save buttons btn_layout = QHBoxLayout() save_text_btn = QPushButton("šŸ’¾ Save Text") save_text_btn.clicked.connect(self._save_text) btn_layout.addWidget(save_text_btn) copy_btn = QPushButton("šŸ“‹ Copy to Clipboard") copy_btn.clicked.connect(self._copy_to_clipboard) btn_layout.addWidget(copy_btn) clear_btn = QPushButton("šŸ—‘ Clear") clear_btn.clicked.connect(self._clear_results) btn_layout.addWidget(clear_btn) btn_layout.addStretch() layout.addLayout(btn_layout) layout.addStretch() return tab def _create_file_test_tab(self): """Create file-based OCR test tab for testing with saved screenshots.""" tab = QWidget() layout = QVBoxLayout(tab) # Info info = QLabel("Test OCR on an image file (PNG, JPG, BMP)") info.setStyleSheet("color: #888;") layout.addWidget(info) # File selection file_layout = QHBoxLayout() self.file_path_label = QLabel("No file selected") self.file_path_label.setStyleSheet("color: #aaa; padding: 8px; background-color: #1a1f2e; border-radius: 4px;") file_layout.addWidget(self.file_path_label, 1) browse_btn = QPushButton("Browse...") browse_btn.setStyleSheet(""" QPushButton { background-color: #4ecdc4; color: #141f23; font-weight: bold; padding: 8px 16px; } """) browse_btn.clicked.connect(self._browse_image_file) file_layout.addWidget(browse_btn) layout.addLayout(file_layout) # Backend selection backend_layout = QHBoxLayout() backend_layout.addWidget(QLabel("OCR Backend:")) self.file_backend_combo = QComboBox() self.file_backend_combo.addItems(["Auto (try all)", "EasyOCR", "Tesseract", "PaddleOCR"]) backend_layout.addWidget(self.file_backend_combo) backend_layout.addStretch() layout.addLayout(backend_layout) # Test button file_test_btn = QPushButton("ā–¶ Run OCR on File") file_test_btn.setStyleSheet(""" QPushButton { background-color: #ff8c42; color: #141f23; font-weight: bold; padding: 12px; font-size: 14px; } """) file_test_btn.clicked.connect(self._run_file_test) layout.addWidget(file_test_btn) # Results results_group = QGroupBox("OCR Results") results_layout = QVBoxLayout(results_group) self.file_results_text = QTextEdit() self.file_results_text.setReadOnly(True) self.file_results_text.setPlaceholderText("OCR results will appear here...") self.file_results_text.setStyleSheet(""" QTextEdit { background-color: #0d1117; color: #c9d1d9; font-family: Consolas, monospace; font-size: 12px; } """) results_layout.addWidget(self.file_results_text) # Stats self.file_stats_label = QLabel("File: - | Backend: - | Status: Waiting") self.file_stats_label.setStyleSheet("color: #888;") results_layout.addWidget(self.file_stats_label) layout.addWidget(results_group, 1) layout.addStretch() return tab def _create_region_test_tab(self): """Create region test tab.""" tab = QWidget() layout = QVBoxLayout(tab) info = QLabel("Test OCR on specific screen region") info.setStyleSheet("color: #888;") layout.addWidget(info) # Region input region_group = QGroupBox("Screen Region (pixels)") region_layout = QHBoxLayout(region_group) self.x_input = QSpinBox() self.x_input.setRange(0, 10000) self.x_input.setValue(100) region_layout.addWidget(QLabel("X:")) region_layout.addWidget(self.x_input) self.y_input = QSpinBox() self.y_input.setRange(0, 10000) self.y_input.setValue(100) region_layout.addWidget(QLabel("Y:")) region_layout.addWidget(self.y_input) self.w_input = QSpinBox() self.w_input.setRange(100, 10000) self.w_input.setValue(400) region_layout.addWidget(QLabel("Width:")) region_layout.addWidget(self.w_input) self.h_input = QSpinBox() self.h_input.setRange(100, 10000) self.h_input.setValue(300) region_layout.addWidget(QLabel("Height:")) region_layout.addWidget(self.h_input) layout.addWidget(region_group) # Presets preset_layout = QHBoxLayout() preset_layout.addWidget(QLabel("Quick Presets:")) presets = [ ("Chat Window", 10, 800, 600, 200), ("Skills Window", 100, 100, 500, 400), ("Inventory", 1200, 200, 600, 500), ("Mission Tracker", 1600, 100, 300, 600), ] for name, x, y, w, h in presets: btn = QPushButton(name) btn.clicked.connect(lambda checked, px=x, py=y, pw=w, ph=h: self._set_region(px, py, pw, ph)) preset_layout.addWidget(btn) preset_layout.addStretch() layout.addLayout(preset_layout) # Test button region_test_btn = QPushButton("ā–¶ Test Region OCR") region_test_btn.setStyleSheet(""" QPushButton { background-color: #4ecdc4; color: #141f23; font-weight: bold; padding: 10px; } """) region_test_btn.clicked.connect(self._run_region_test) layout.addWidget(region_test_btn) # Results self.region_results = QTextEdit() self.region_results.setReadOnly(True) self.region_results.setPlaceholderText("Region OCR results will appear here...") self.region_results.setStyleSheet(""" QTextEdit { background-color: #0d1117; color: #c9d1d9; font-family: Consolas, monospace; } """) layout.addWidget(self.region_results) layout.addStretch() return tab def _create_history_tab(self): """Create history tab.""" tab = QWidget() layout = QVBoxLayout(tab) info = QLabel("History of OCR tests") info.setStyleSheet("color: #888;") layout.addWidget(info) self.history_text = QTextEdit() self.history_text.setReadOnly(True) self.history_text.setStyleSheet(""" QTextEdit { background-color: #0d1117; color: #c9d1d9; } """) layout.addWidget(self.history_text) # Controls btn_layout = QHBoxLayout() refresh_btn = QPushButton("šŸ”„ Refresh") refresh_btn.clicked.connect(self._update_history) btn_layout.addWidget(refresh_btn) clear_hist_btn = QPushButton("šŸ—‘ Clear History") clear_hist_btn.clicked.connect(self._clear_history) btn_layout.addWidget(clear_hist_btn) btn_layout.addStretch() layout.addLayout(btn_layout) return tab def _create_calibration_tab(self): """Create calibration tab.""" tab = QWidget() layout = QVBoxLayout(tab) info = QLabel("Calibration tools for optimizing OCR accuracy") info.setStyleSheet("color: #888;") layout.addWidget(info) # DPI awareness dpi_group = QGroupBox("Display Settings") dpi_layout = QVBoxLayout(dpi_group) self.dpi_label = QLabel("Detecting display DPI...") dpi_layout.addWidget(self.dpi_label) detect_dpi_btn = QPushButton("Detect Display Settings") detect_dpi_btn.clicked.connect(self._detect_display_settings) dpi_layout.addWidget(detect_dpi_btn) layout.addWidget(dpi_group) # Backend status backend_group = QGroupBox("OCR Backend Status") backend_layout = QVBoxLayout(backend_group) self.backend_status = QTextEdit() self.backend_status.setReadOnly(True) self.backend_status.setMaximumHeight(200) self._check_backends() backend_layout.addWidget(self.backend_status) # Install buttons install_layout = QHBoxLayout() install_easyocr_btn = QPushButton("šŸ“¦ Install EasyOCR") install_easyocr_btn.setStyleSheet(""" QPushButton { background-color: #4ecdc4; color: #141f23; font-weight: bold; padding: 8px; } """) install_easyocr_btn.clicked.connect(self._install_easyocr) install_layout.addWidget(install_easyocr_btn) install_tesseract_btn = QPushButton("šŸ“¦ Install Tesseract Package") install_tesseract_btn.setStyleSheet(""" QPushButton { background-color: #ff8c42; color: #141f23; font-weight: bold; padding: 8px; } """) install_tesseract_btn.clicked.connect(self._install_pytesseract) install_layout.addWidget(install_tesseract_btn) detect_tesseract_btn = QPushButton("šŸ” Auto-Detect Tesseract") detect_tesseract_btn.setStyleSheet(""" QPushButton { background-color: #a0aec0; color: #141f23; font-weight: bold; padding: 8px; } """) detect_tesseract_btn.clicked.connect(self._auto_detect_tesseract) install_layout.addWidget(detect_tesseract_btn) install_paddle_btn = QPushButton("šŸ“¦ Install PaddleOCR") install_paddle_btn.setStyleSheet(""" QPushButton { background-color: #4a5568; color: white; font-weight: bold; padding: 8px; } """) install_paddle_btn.clicked.connect(self._install_paddleocr) install_layout.addWidget(install_paddle_btn) backend_layout.addLayout(install_layout) layout.addWidget(backend_group) # Tips tips_group = QGroupBox("Tips for Best Results") tips_layout = QVBoxLayout(tips_group) tips_text = QLabel(""" • Make sure text is clearly visible and not blurry • Use region selection to focus on specific text areas • Higher resolution screens work better for OCR • Close other windows to reduce background noise • Ensure good contrast between text and background """) tips_text.setWordWrap(True) tips_text.setStyleSheet("color: #aaa;") tips_layout.addWidget(tips_text) layout.addWidget(tips_group) layout.addStretch() return tab def _set_region(self, x, y, w, h): """Set region values.""" self.x_input.setValue(x) self.y_input.setValue(y) self.w_input.setValue(w) self.h_input.setValue(h) def _browse_image_file(self): """Browse for an image file to test OCR on.""" file_path, _ = QFileDialog.getOpenFileName( None, "Select Image File", "", "Images (*.png *.jpg *.jpeg *.bmp *.tiff);;All Files (*)" ) if file_path: self.selected_file_path = file_path self.file_path_label.setText(Path(file_path).name) self.file_path_label.setStyleSheet("color: #4ecdc4; padding: 8px; background-color: #1a1f2e; border-radius: 4px;") def _run_file_test(self): """Run OCR on the selected image file.""" if not hasattr(self, 'selected_file_path') or not self.selected_file_path: QMessageBox.warning(None, "No File", "Please select an image file first!") return backend_map = { 0: 'auto', 1: 'easyocr', 2: 'tesseract', 3: 'paddle' } backend = backend_map.get(self.file_backend_combo.currentIndex(), 'auto') self.file_results_text.setPlainText("Processing...") self.file_stats_label.setText("Processing...") # Run OCR in a thread from threading import Thread def process_file(): try: from PIL import Image import time start_time = time.time() # Load image image = Image.open(self.selected_file_path) # Try OCR backends text = "" backend_used = "none" if backend in ('auto', 'easyocr'): try: import easyocr import numpy as np reader = easyocr.Reader(['en'], gpu=False, verbose=False) # Convert PIL Image to numpy array for EasyOCR image_np = np.array(image) ocr_result = reader.readtext( image_np, detail=0, paragraph=True ) text = '\n'.join(ocr_result) backend_used = "easyocr" except Exception as e: if backend == 'easyocr': raise e if not text and backend in ('auto', 'tesseract'): try: import pytesseract text = pytesseract.image_to_string(image) backend_used = "tesseract" except Exception as e: if backend == 'tesseract': error_msg = str(e) if "tesseract is not installed" in error_msg.lower() or "not in your path" in error_msg.lower(): raise Exception( "Tesseract is not installed.\n\n" "To use Tesseract OCR:\n" "1. Download from: https://github.com/UB-Mannheim/tesseract/wiki\n" "2. Install to C:\\Program Files\\Tesseract-OCR\\\n" "3. Add to PATH or restart EU-Utility\n\n" "Alternatively, use EasyOCR (auto-installs): pip install easyocr" ) raise e if not text and backend in ('auto', 'paddle'): try: from paddleocr import PaddleOCR # Try without show_log argument for compatibility try: ocr = PaddleOCR(use_angle_cls=True, lang='en', show_log=False) except TypeError: # Older version without show_log ocr = PaddleOCR(use_angle_cls=True, lang='en') # Convert PIL to numpy for PaddleOCR import numpy as np image_np = np.array(image) result = ocr.ocr(image_np, cls=True) if result and result[0]: texts = [line[1][0] for line in result[0]] text = '\n'.join(texts) backend_used = "paddleocr" except Exception as e: if backend == 'paddle': raise e processing_time = time.time() - start_time # Update UI (thread-safe via Qt signals would be better, but this works for simple case) from PyQt6.QtCore import QMetaObject, Qt, Q_ARG QMetaObject.invokeMethod( self.file_results_text, "setPlainText", Qt.ConnectionType.QueuedConnection, Q_ARG(str, text if text else "No text detected") ) QMetaObject.invokeMethod( self.file_stats_label, "setText", Qt.ConnectionType.QueuedConnection, Q_ARG(str, f"File: {Path(self.selected_file_path).name} | Backend: {backend_used} | Time: {processing_time:.2f}s") ) except Exception as e: from PyQt6.QtCore import QMetaObject, Qt, Q_ARG QMetaObject.invokeMethod( self.file_results_text, "setPlainText", Qt.ConnectionType.QueuedConnection, Q_ARG(str, f"Error: {str(e)}") ) thread = Thread(target=process_file) thread.daemon = True thread.start() def _run_quick_test(self): """Run quick OCR test.""" if self.ocr_thread and self.ocr_thread.isRunning(): return backend_map = { 0: 'auto', 1: 'easyocr', 2: 'tesseract', 3: 'paddle' } backend = backend_map.get(self.backend_combo.currentIndex(), 'auto') self.test_btn.setEnabled(False) self.test_btn.setText("ā³ Running...") self.progress_bar.setValue(0) self.ocr_thread = OCRTestThread(backend=backend) self.ocr_thread.progress_update.connect(self._update_progress) self.ocr_thread.result_ready.connect(self._on_ocr_complete) self.ocr_thread.start() def _update_progress(self, value, message): """Update progress bar.""" self.progress_bar.setValue(value) self.status_label.setText(message) def _on_ocr_complete(self, results): """Handle OCR completion.""" self.test_btn.setEnabled(True) self.test_btn.setText("ā–¶ Run OCR Test") if results['success']: self.results_text.setPlainText(results['text']) self.stats_label.setText( f"Backend: {results['backend_used']} | " f"Time: {results['processing_time']:.2f}s | " f"Status: āœ… Success" ) # Add to history self._add_to_history(results) else: self.results_text.setPlainText(f"Error: {results.get('error', 'Unknown error')}") self.stats_label.setText(f"Backend: {results.get('backend_used', '-')} | Status: āŒ Failed") def _run_region_test(self): """Run region OCR test.""" region = ( self.x_input.value(), self.y_input.value(), self.w_input.value(), self.h_input.value() ) self.region_results.setPlainText("Running OCR on region...") # Use api's ocr_capture if available try: result = self.ocr_capture(region=region) self.region_results.setPlainText(result.get('text', 'No text detected')) except Exception as e: self.region_results.setPlainText(f"Error: {e}") def _save_text(self): """Save OCR text to file.""" text = self.results_text.toPlainText() if not text: QMessageBox.warning(None, "Save Error", "No text to save!") return file_path, _ = QFileDialog.getSaveFileName( None, "Save OCR Text", "ocr_result.txt", "Text Files (*.txt)" ) if file_path: with open(file_path, 'w', encoding='utf-8') as f: f.write(text) self.status_label.setText(f"Saved to {file_path}") def _copy_to_clipboard(self): """Copy text to clipboard.""" text = self.results_text.toPlainText() if text: self.copy_to_clipboard(text) self.status_label.setText("Copied to clipboard!") def _clear_results(self): """Clear results.""" self.results_text.clear() self.stats_label.setText("Backend: - | Time: - | Status: Waiting") self.progress_bar.setValue(0) def _add_to_history(self, results): """Add result to history.""" from datetime import datetime entry = { 'time': datetime.now().strftime("%H:%M:%S"), 'backend': results['backend_used'], 'time_taken': results['processing_time'], 'text_preview': results['text'][:100] + "..." if len(results['text']) > 100 else results['text'] } self.test_history.insert(0, entry) if len(self.test_history) > self.max_history: self.test_history = self.test_history[:self.max_history] self._update_history() def _update_history(self): """Update history display.""" lines = [] for entry in self.test_history: lines.append(f"[{entry['time']}] {entry['backend']} ({entry['time_taken']:.2f}s)") lines.append(f" {entry['text_preview']}") lines.append("") self.history_text.setPlainText('\n'.join(lines) if lines else "No history yet") def _clear_history(self): """Clear history.""" self.test_history.clear() self._update_history() def _detect_display_settings(self): """Detect display settings.""" try: from PyQt6.QtWidgets import QApplication from PyQt6.QtGui import QScreen app = QApplication.instance() if app: screens = app.screens() info = [] for i, screen in enumerate(screens): geo = screen.geometry() dpi = screen.logicalDotsPerInch() info.append(f"Screen {i+1}: {geo.width()}x{geo.height()} @ {dpi:.0f} DPI") self.dpi_label.setText('\n'.join(info)) except Exception as e: self.dpi_label.setText(f"Error: {e}") def _install_easyocr(self): """Install EasyOCR backend.""" from core.ocr_backend_manager import get_ocr_backend_manager manager = get_ocr_backend_manager() success, message = manager.install_backend('easyocr') if success: self.notify_success("Installation Complete", message) else: self.notify_error("Installation Failed", message) self._check_backends() def _install_pytesseract(self): """Install pytesseract Python package.""" from core.ocr_backend_manager import get_ocr_backend_manager manager = get_ocr_backend_manager() success, message = manager.install_backend('tesseract') if success: self.notify_success("Installation Complete", message + "\n\nNote: You also need to install the Tesseract binary from:\nhttps://github.com/UB-Mannheim/tesseract/wiki") else: self.notify_error("Installation Failed", message) self._check_backends() def _install_paddleocr(self): """Install PaddleOCR backend.""" from core.ocr_backend_manager import get_ocr_backend_manager manager = get_ocr_backend_manager() success, message = manager.install_backend('paddleocr') if success: self.notify_success("Installation Complete", message) else: self.notify_error("Installation Failed", message) self._check_backends() def _auto_detect_tesseract(self): """Auto-detect Tesseract from registry/paths.""" from core.ocr_backend_manager import get_ocr_backend_manager manager = get_ocr_backend_manager() if manager.auto_configure_tesseract(): self.notify_success("Tesseract Found", f"Auto-configured Tesseract at:\n{manager.backends['tesseract']['path']}") else: self.notify_warning("Not Found", "Could not find Tesseract installation.\n\nPlease install from:\nhttps://github.com/UB-Mannheim/tesseract/wiki") self._check_backends() def _check_backends(self): """Check OCR backend availability with install buttons.""" from core.ocr_backend_manager import get_ocr_backend_manager manager = get_ocr_backend_manager() statuses = [] # EasyOCR easyocr_status = manager.get_backend_status('easyocr') if easyocr_status['available']: statuses.append("āœ… EasyOCR - Available (recommended)") else: statuses.append("āŒ EasyOCR - Not installed\n Click 'Install EasyOCR' button below") # Tesseract tesseract_status = manager.get_backend_status('tesseract') if tesseract_status['available']: path_info = f" at {tesseract_status['path']}" if tesseract_status['path'] else "" statuses.append(f"āœ… Tesseract - Available{path_info}") elif tesseract_status['installed']: statuses.append("āš ļø Tesseract - Python package installed but binary not found\n Click 'Auto-Detect Tesseract' or install binary from:\n https://github.com/UB-Mannheim/tesseract/wiki") else: statuses.append("āŒ Tesseract - Not installed\n Click 'Install Tesseract Package' then install binary") # PaddleOCR paddle_status = manager.get_backend_status('paddleocr') if paddle_status['available']: statuses.append("āœ… PaddleOCR - Available") else: statuses.append("āŒ PaddleOCR - Not installed\n Click 'Install PaddleOCR' button below") statuses.append("\nšŸ’” Recommendation: EasyOCR is easiest (auto-downloads models)") self.backend_status.setPlainText('\n'.join(statuses)) def shutdown(self): """Clean up.""" if self.ocr_thread and self.ocr_thread.isRunning(): self.ocr_thread.wait(1000) super().shutdown()