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:
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user