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) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 22:56:50 +01:00
parent 29cb993b7b
commit defb5d101e

View File

@@ -3,8 +3,9 @@
import os import os
import threading import threading
import time import time
import queue
from flask import Flask, request, jsonify, render_template_string 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") UPLOAD_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "data", "photos")
ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png", "bmp", "tiff", "tif", "webp", "heic"} ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png", "bmp", "tiff", "tif", "webp", "heic"}
@@ -280,10 +281,16 @@ class UploadServer:
self.port = port self.port = port
self.signals = UploadSignals() self.signals = UploadSignals()
self._callback = None self._callback = None
self._queue = queue.Queue()
self.app = Flask(__name__) self.app = Flask(__name__)
self._setup_routes() self._setup_routes()
self._thread = None 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) os.makedirs(UPLOAD_DIR, exist_ok=True)
def _setup_routes(self): def _setup_routes(self):
@@ -309,11 +316,7 @@ class UploadServer:
saved.append(path) saved.append(path)
if saved: if saved:
# QTimer.singleShot est thread-safe et exécute dans le thread principal self._queue.put(saved)
from PyQt6.QtCore import QTimer
from functools import partial
if self._callback:
QTimer.singleShot(0, partial(self._callback, saved))
return jsonify({"ok": True, "count": len(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.""" """Définit le callback appelé quand des photos sont uploadées."""
self._callback = callback 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): def start(self):
"""Démarre le serveur dans un thread daemon.""" """Démarre le serveur dans un thread daemon."""
self._thread = threading.Thread( self._thread = threading.Thread(