diff --git a/core/camera/camera_controller.py b/core/camera/camera_controller.py index 826a367..bc46ec1 100644 --- a/core/camera/camera_controller.py +++ b/core/camera/camera_controller.py @@ -5,11 +5,7 @@ import cv2 from .base_camera import BaseCamera -class CameraController(QObject): - """ - A QObject worker for handling camera operations in a separate thread. - This object should be moved to a QThread. - """ +class CameraWorker(QObject): frame_ready = Signal(QPixmap) photo_ready = Signal(QPixmap) error_occurred = Signal(str) @@ -24,11 +20,8 @@ class CameraController(QObject): self._camera_mutex = QMutex() @Slot() - def run(self): - """ - Initializes resources in the worker thread. - This should be connected to the QThread.started signal. - """ + def initialize_worker(self): + """Initializes the timer in the worker's thread.""" self.timer = QTimer() self.timer.timeout.connect(self._update_frame) @@ -68,23 +61,20 @@ class CameraController(QObject): @Slot() def start_stream(self): - with QMutexLocker(self._camera_mutex): - if not self.is_connected or self.is_streaming or self.timer is None: - return + if not self.is_connected or self.is_streaming or self.timer is None: + return - self.is_streaming = True - self.timer.setInterval(int(1000 / self.fps)) - self.timer.start() + self.is_streaming = True + self.timer.setInterval(int(1000 / self.fps)) + self.timer.start() @Slot() def stop_stream(self) -> None: - with QMutexLocker(self._camera_mutex): - if not self.is_streaming or self.timer is None: - return - + if self.is_streaming and self.timer is not None: self.is_streaming = False self.timer.stop() + @Slot() def _update_frame(self) -> None: # This method is called by the timer, which is in the same thread. # A mutex is still good practice for accessing the shared camera object. @@ -107,4 +97,65 @@ class CameraController(QObject): qimg = QImage(rgb_image.data, w, h, ch * w, QImage.Format.Format_RGB888) pixmap = QPixmap.fromImage(qimg) self.frame_ready.emit(pixmap) - + + def isConnected(self): + return self.is_connected + + +class CameraController(QObject): + frame_ready = Signal(QPixmap) + photo_ready = Signal(QPixmap) + error_occurred = Signal(str) + + # Signals to command the worker + _set_camera_requested = Signal(BaseCamera, int) + _start_camera_requested = Signal() + _stop_camera_requested = Signal() + _start_stream_requested = Signal() + _stop_stream_requested = Signal() + + def __init__(self, parent: QObject | None = None) -> None: + super().__init__(parent) + self._thread = QThread() + self._worker = CameraWorker() + + self._worker.moveToThread(self._thread) + + # Connect worker signals to controller signals + self._worker.frame_ready.connect(self.frame_ready) + self._worker.photo_ready.connect(self.photo_ready) + self._worker.error_occurred.connect(self.error_occurred) + + # Connect controller's command signals to worker's slots + self._set_camera_requested.connect(self._worker.set_camera) + self._start_camera_requested.connect(self._worker.start_camera) + self._stop_camera_requested.connect(self._worker.stop_camera) + self._start_stream_requested.connect(self._worker.start_stream) + self._stop_stream_requested.connect(self._worker.stop_stream) + + # Initialize worker when thread starts + self._thread.started.connect(self._worker.initialize_worker) + + self._thread.start() + + def stop(self): + self._thread.quit() + self._thread.wait() + + def set_camera(self, camera: BaseCamera, fps: int = 15) -> None: + self._set_camera_requested.emit(camera, fps) + + def start_camera(self) -> None: + self._start_camera_requested.emit() + + def stop_camera(self) -> None: + self._stop_camera_requested.emit() + + def start_stream(self): + self._start_stream_requested.emit() + + def stop_stream(self) -> None: + self._stop_stream_requested.emit() + + def is_connected(self): + return self._worker.isConnected() \ No newline at end of file