#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Test de Capture d'Écran Corrigé - RPA Vision V3 Auteur : Dom, Alice, Kiro - 8 janvier 2026 Version corrigée pour les problèmes de threading avec MSS. """ import cv2 import numpy as np import mss import base64 import io from PIL import Image import threading import time import logging class FixedRealScreenCaptureService: """ Service de capture d'écran avec correction des problèmes de threading """ def __init__(self): self.is_capturing = False self.capture_thread = None self.current_screenshot = None self.detected_elements = [] self.capture_interval = 1.0 self.monitors = [] self.selected_monitor = 0 # Initialiser MSS dans le thread principal self._init_mss() def _init_mss(self): """Initialise MSS dans le thread principal""" try: with mss.mss() as sct: self.monitors = sct.monitors print(f"Détecté {len(self.monitors)} moniteurs") for i, monitor in enumerate(self.monitors): print(f"Moniteur {i}: {monitor}") except Exception as e: print(f"Erreur lors de l'initialisation MSS: {e}") self.monitors = [{"top": 0, "left": 0, "width": 1920, "height": 1080}] def get_monitors(self): """Retourne la liste des moniteurs disponibles""" return [ { "id": i, "width": monitor.get("width", 0), "height": monitor.get("height", 0), "top": monitor.get("top", 0), "left": monitor.get("left", 0) } for i, monitor in enumerate(self.monitors) ] def select_monitor(self, monitor_id: int) -> bool: """Sélectionne le moniteur à capturer""" if 0 <= monitor_id < len(self.monitors): self.selected_monitor = monitor_id print(f"Moniteur sélectionné: {monitor_id}") return True return False def start_capture(self, interval: float = 1.0) -> bool: """Démarre la capture d'écran en temps réel""" if self.is_capturing: print("Capture déjà en cours") return False self.capture_interval = interval self.is_capturing = True # Démarrer le thread de capture self.capture_thread = threading.Thread(target=self._capture_loop, daemon=True) self.capture_thread.start() print(f"Capture démarrée (intervalle: {interval}s)") return True def stop_capture(self) -> bool: """Arrête la capture d'écran""" if not self.is_capturing: return False self.is_capturing = False if self.capture_thread and self.capture_thread.is_alive(): self.capture_thread.join(timeout=2.0) print("Capture arrêtée") return True def _capture_loop(self): """Boucle principale de capture avec MSS local au thread""" # Créer une instance MSS locale au thread try: with mss.mss() as sct: while self.is_capturing: try: # Capturer l'écran screenshot = self._capture_screen_with_sct(sct) if screenshot is not None: self.current_screenshot = screenshot print(f"Screenshot capturé: {screenshot.shape}") # Attendre avant la prochaine capture time.sleep(self.capture_interval) except Exception as e: print(f"Erreur dans la boucle de capture: {e}") time.sleep(1.0) except Exception as e: print(f"Erreur lors de l'initialisation MSS dans le thread: {e}") def _capture_screen_with_sct(self, sct): """Capture l'écran avec une instance MSS donnée""" try: if self.selected_monitor >= len(self.monitors): self.selected_monitor = 0 monitor = self.monitors[self.selected_monitor] # Capturer avec MSS screenshot = sct.grab(monitor) # Convertir en array numpy img_array = np.array(screenshot) # Convertir BGRA vers BGR (OpenCV) if img_array.shape[2] == 4: img_array = cv2.cvtColor(img_array, cv2.COLOR_BGRA2BGR) return img_array except Exception as e: print(f"Erreur lors de la capture d'écran: {e}") return None def capture_single_screenshot(self): """Capture une seule image (pour test synchrone)""" try: with mss.mss() as sct: return self._capture_screen_with_sct(sct) except Exception as e: print(f"Erreur lors de la capture unique: {e}") return None def get_current_screenshot_base64(self): """Retourne la capture d'écran actuelle en base64""" if self.current_screenshot is None: return None try: # Convertir en PIL Image if len(self.current_screenshot.shape) == 3: # BGR vers RGB rgb_image = cv2.cvtColor(self.current_screenshot, cv2.COLOR_BGR2RGB) pil_image = Image.fromarray(rgb_image) else: pil_image = Image.fromarray(self.current_screenshot) # Redimensionner pour l'affichage web max_width = 1200 if pil_image.width > max_width: ratio = max_width / pil_image.width new_height = int(pil_image.height * ratio) pil_image = pil_image.resize((max_width, new_height), Image.Resampling.LANCZOS) # Convertir en base64 buffer = io.BytesIO() pil_image.save(buffer, format='JPEG', quality=85) img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8') return f"data:image/jpeg;base64,{img_base64}" except Exception as e: print(f"Erreur lors de la conversion base64: {e}") return None def get_status(self): """Retourne le statut du service""" return { "is_capturing": self.is_capturing, "selected_monitor": self.selected_monitor, "monitors_count": len(self.monitors), "capture_interval": self.capture_interval, "elements_detected": len(self.detected_elements), "has_screenshot": self.current_screenshot is not None } def cleanup(self): """Nettoie les ressources""" self.stop_capture() def test_service_fixe(): """Test du service corrigé""" print("=== Test du Service de Capture Corrigé ===") service = FixedRealScreenCaptureService() try: # 1. Test des moniteurs monitors = service.get_monitors() print(f"✅ Moniteurs détectés: {len(monitors)}") # 2. Test de capture unique (synchrone) print("Test de capture unique...") screenshot = service.capture_single_screenshot() if screenshot is not None: print(f"✅ Capture unique réussie: {screenshot.shape}") # Convertir en base64 pour test service.current_screenshot = screenshot base64_img = service.get_current_screenshot_base64() if base64_img: print(f"✅ Conversion base64 réussie: {len(base64_img)} caractères") else: print("❌ Échec conversion base64") else: print("❌ Échec capture unique") # 3. Test de capture en continu print("\nTest de capture en continu...") success = service.start_capture(interval=1.0) print(f"✅ Démarrage capture: {success}") # Attendre quelques captures print("Attente de 5 secondes...") time.sleep(5.0) # Vérifier le statut status = service.get_status() print(f"✅ Statut: {status}") # Obtenir une capture screenshot_b64 = service.get_current_screenshot_base64() if screenshot_b64: print(f"✅ Screenshot en continu: {len(screenshot_b64)} caractères") else: print("❌ Aucun screenshot en continu") # Arrêter la capture success = service.stop_capture() print(f"✅ Arrêt capture: {success}") return screenshot is not None except Exception as e: print(f"❌ Erreur lors du test: {e}") import traceback traceback.print_exc() return False finally: service.cleanup() if __name__ == "__main__": print("🔍 Test du Service de Capture d'Écran Corrigé") print("=" * 50) if test_service_fixe(): print("\n🎉 Service de capture entièrement fonctionnel !") else: print("\n❌ Problème persistant avec le service de capture")