From defb5d101e448004947a5bd212a993bf0614382c Mon Sep 17 00:00:00 2001 From: Jules Date: Sat, 21 Mar 2026 22:56:50 +0100 Subject: [PATCH] Fix cross-thread : queue + QTimer polling au lieu de signal Thread Flask met les photos dans une queue.Queue(). QTimer(500ms) dans le thread principal Qt poll la queue et appelle le callback. Approche fiable et simple. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/services/upload_server.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/services/upload_server.py b/src/services/upload_server.py index 5152a92..00ecb48 100644 --- a/src/services/upload_server.py +++ b/src/services/upload_server.py @@ -3,8 +3,9 @@ import os import threading import time +import queue from flask import Flask, request, jsonify, render_template_string -from PyQt6.QtCore import QObject, pyqtSignal +from PyQt6.QtCore import QObject, pyqtSignal, QTimer UPLOAD_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "data", "photos") ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png", "bmp", "tiff", "tif", "webp", "heic"} @@ -280,10 +281,16 @@ class UploadServer: self.port = port self.signals = UploadSignals() self._callback = None + self._queue = queue.Queue() self.app = Flask(__name__) self._setup_routes() self._thread = None + # Timer Qt qui poll la queue depuis le thread principal + self._poll_timer = QTimer() + self._poll_timer.timeout.connect(self._poll_queue) + self._poll_timer.start(500) + os.makedirs(UPLOAD_DIR, exist_ok=True) def _setup_routes(self): @@ -309,11 +316,7 @@ class UploadServer: saved.append(path) if saved: - # QTimer.singleShot est thread-safe et exécute dans le thread principal - from PyQt6.QtCore import QTimer - from functools import partial - if self._callback: - QTimer.singleShot(0, partial(self._callback, saved)) + self._queue.put(saved) return jsonify({"ok": True, "count": len(saved)}) @@ -321,6 +324,16 @@ class UploadServer: """Définit le callback appelé quand des photos sont uploadées.""" self._callback = callback + def _poll_queue(self): + """Appelé par QTimer dans le thread principal Qt.""" + while not self._queue.empty(): + try: + photos = self._queue.get_nowait() + if self._callback: + self._callback(photos) + except queue.Empty: + break + def start(self): """Démarre le serveur dans un thread daemon.""" self._thread = threading.Thread(